i need help with redux toolkit, i'm using redux to fetch one array of objects but this not happen bellow is my redux code and get api.
GET API
export const getProductsApi = async () => {
const response = await fetch(
`${gateway.url}/products`,
{
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
}
);
const data = await response.json();
return data;
}
MY REDUX ACTION
import {
getProductsPending,
getProductsSuccess,
getProductsFailed,
} from "../../slices/get-products";
import { getProductsApi } from "../../../api/get-products";
export const getProducts = () => async (dispatch: any) => {
dispatch(getProductsPending());
try {
const response = await getProductsApi();
const data = response.data;
console.log('products redux here', data);
dispatch(getProductsSuccess(data));
} catch (error) {
dispatch(getProductsFailed(error));
}
};
REDUX SLICE
const getProductsSlice = createSlice({
name: "getProducts",
initialState,
reducers: {
getProductsPending: (state) => {
state.isLoading = true;
state.error = "";
},
getProductsSuccess: (state, action: PayloadAction<Product[]>) => {
state.isLoading = false;
state.products = action.payload;
state.error = "";
},
getProductsFailed: (state, action) => {
state.isLoading = false;
state.error = action.payload.error;
state.products = [];
},
},
});
const { reducer, actions } = getProductsSlice;
export const { getProductsPending, getProductsSuccess, getProductsFailed } =
actions;
export default reducer;
REDUX STORE
import { configureStore } from "#reduxjs/toolkit";
import getProductsReducer from "../redux/slices/get-products";
const store = configureStore({
reducer: {
products: getProductsReducer,
},
});
store.subscribe(() => console.log(store.getState()));
export default store;
in my browser console when is to appear the data, data appear so
whit my products array empty, and i don't know where is my error because anothers redux i made, i make like this and work correctly.
I appreciate every help to solve my question.
using redux tookit
Related
Profile Slice:
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit'
import { IMAGE_API, ACCESS_KEY } from "../../app/utils/constant";
export const getImages = createAsyncThunk('images', async () => {
return fetch(`${IMAGE_API + ACCESS_KEY}`).then((res) =>
res.json()
)
})
console.log(IMAGE_API + ACCESS_KEY);
const ProfilePicSlice = createSlice({
name: 'imageList',
initialState: {
images: [],
loading: false,
},
extraReducers: (builder) => {
builder.addCase(getImages.pending, (state) => {
state.loading = true;
})
builder.addCase(getImages.fulfilled, (state, action) => {
state.loading = false;
state.images.push(action.payload);
console.log(action.payload)
})
builder.addCase(getImages.rejected, (state) => {
state.loading = true;
})
}
});
export default ProfilePicSlice.reducer
Form Slice:
import { createSlice } from "#reduxjs/toolkit";
const initialState = []
const UserSlice = createSlice({
name: 'users',
initialState,
reducers: {
addUser: (state, action) => {
state.push(action.payload);
}
}
});
export const {addUser} = UserSlice.actions;
export default UserSlice.reducer;
I want to add custom param in API URL in asyncThunk '${IMAGE_API + 'custom param' + ACCESS_KEY}
custom param should come from form slice data.
redux-toolkit async thunks are able to access the ThunkAPI, which includes a function to get the current redux state object, it is the second argument passed to the thunk callback. From here you can select the state value you need.
See PayloadCreator.
Example:
export const getImages = createAsyncThunk(
'imageList /images',
(_, { getState }) => {
const store = getState();
const param = state.users./* access into state to get property */;
return fetch(`${IMAGE_API}${param}${ACCESS_KEY}`)
.then((res) => res.json());
}
);
I am struggling with an issue with the custom fetch hook.Simply i am trying to test my fetch hook if the data already fetched the hook needs to get data from cache instead of api.
The test case fails and looks like caching mechanism not working, but if i try on the browser with manual prop change caching mechanism works properly.
import { render, waitFor } from "#testing-library/react";
const renderList = (filterParams = testFilterParamsList[0]) =>
render(<List filterParams={filterParams} />);
it("should re-render without fetch", async () => {
const { rerender } = renderList(testFilterParamsList[0]);
rerender(<List filterParams={testFilterParamsList[1]} />);
expect(window.fetch).toHaveBeenCalledTimes(1);
});
// useFetch.js
import {useEffect, useReducer} from "react";
const cache = {};
const FETCH_REQUEST = "FETCH_REQUEST";
const FETCH_SUCCESS = "FETCH_SUCCESS";
const FETCH_ERROR = "FETCH_SUCCESS";
const INITIAL_STATE = {
isPending: false,
error: null,
data: [],
};
const useFetch = ({url, filterOptions}) => {
const [state, dispatch] = useReducer((state, action) => {
switch (action.type) {
case FETCH_REQUEST:return {...INITIAL_STATE, isPending: true};
case FETCH_SUCCESS: return {...INITIAL_STATE, isPending: false, data: action.payload};
case FETCH_ERROR: return {...INITIAL_STATE, isPending: false, error: action.payload};
default: return state;
}
}, INITIAL_STATE);
useEffect(() => {
const fetchData = async () => {
dispatch({type: FETCH_REQUEST});
if (cache[url]) {
const data = cache[url];
dispatch({type: FETCH_SUCCESS, payload: data});
} else {
try {
const response = await window.fetch(url);
let data = await response.json();
cache[url] = data
dispatch({type: FETCH_SUCCESS, payload: data});
} catch (err) {
dispatch({type: FETCH_ERROR, payload: err});
}
}
};
fetchData();
}, [filterOptions, url]);
return state;
};
export default useFetch;
// List.js
import useFetch from "../hooks/useFetch";
export const RocketsList = ({ filterParams }) => {
const { isPending, error, data } = useFetch({
url: "https://api.spacexdata.com/v3/launches/past",
name:filterParams.name,
});
return (
<div>
Doesn't matter
</div>
);
};
I understand this has been asked before but so far no answers except someone making a syntax mistake. I have verified this against every tutorial I've seen online and I can't find the issue. It seems to be coming from the fullfilled extraReducer
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import axios from "axios";
const KEY = process.env.REACT_APP_API_KEY
const BASE_URL = process.env.REACT_APP_BASE_URL
const GLOBALVIEWS_API = `${BASE_URL}/countrymap`
const initialState = {
mapdata:{},
status: 'idle', //'idle', 'loading', 'succeeded', 'failed'
error:null
}
export const fetchMapData = createAsyncThunk(
'mapdata/fetchMapData',
async (id) => {
const response = await axios.get(
GLOBALVIEWS_API,
{
headers: {
'Content-Type': 'application/json',
'X-API-KEY': KEY,
},
params: {
titleId: id,
}
}
)
return response.data.Item;
}
)
const mapSlice = createSlice({
name: 'mapdata',
initialState,
reducers:{
addMapData: (state, { payload }) => {
state.mapdata = payload
}
},
extraReducers: {
[fetchMapData.pending]: () => {
console.log("Pending");
},
[fetchMapData.fulfilled]: (state, { payload }) => {
state.status = 'succeeded'
console.log("Succeeded", payload);
return {...state, mapdata: payload}
},
[fetchMapData.rejected]: () => {
console.log("Rejected");
},
}
})
// SELECTORS
export const getMapStatus = (state) => state.mapdata.status;
export const allMapData = (state) => state.mapdata.mapdata;
export const { addMapData } = mapSlice.actions;
export default mapSlice.reducer
In the component, nothing weird, and yes everything is imported
const {id} = useParams();
const dispatch = useDispatch();
const allmapdata = useSelector(allMapData)
const addmapdata = useSelector(addMapData)
const mapStatus = useSelector(getMapStatus)
useEffect(() => {
dispatch(fetchMapData(id))
lazy(setMap(allmapdata))
console.clear()
console.log("mapdata: ", allmapdata);
}, [id, allmapdata, dispatch])
You can see in my image below that my fetch is successful and I am getting data. So how do I get rid of this error? Thanks in advance.
This is the issue:
state.status = 'succeeded'
console.log("Succeeded", payload);
return {...state, mapdata: payload}
That is indeed both a "mutation of the existing state" and a "return of a new value", and that's exactly what the error is warning you about.
You can change that to:
state.status = 'succeeded'
state.mapdata = action.payload
I am creating a simple app to play with Redux ToolKit along react; however, I can see the object in the redux chrome tab, but unable to access it through components using react hooks.
My slice:
import {
createSlice,
createSelector,
createAsyncThunk,
} from "#reduxjs/toolkit";
const initialState = {
cryptoList: [],
loading: false,
hasErrors: false,
};
export const getCryptos = createAsyncThunk("cryptos/get", async (thunkAPI) => {
try {
const cryptosUrl =
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd";
let response = await fetch(cryptosUrl);
console.log(response);
return await response.json();
} catch (error) {
console.log(error);
}
});
const cryptoSlice = createSlice({
name: "cryptos",
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(getCryptos.pending, (state) => {
state.loading = true;
});
builder.addCase(getCryptos.fulfilled, (state, { payload }) => {
state.loading = false;
state.cryptoList = payload;
});
builder.addCase(getCryptos.rejected, (state, action) => {
state.loading = false;
state.hasErrors = action.error.message;
});
},
});
export const selectCryptos = createSelector(
(state) => ({
cryptoList: state.cryptoList,
loading: state.loading,
}),
(state) => state
);
export default cryptoSlice;
My component:
import React, { useEffect } from "react";
import { getCryptos, selectCryptos } from "./cryptoSlice";
import { useSelector, useDispatch } from "react-redux";
const CryptoComponent = () => {
const dispatch = useDispatch();
const { cryptoList, loading, hasErrors } = useSelector(selectCryptos);
useEffect(() => {
dispatch(getCryptos());
}, [dispatch]);
const renderCrypto = () => {
if (loading) return <p>Loading Crypto...</p>;
if (hasErrors) return <p>Error loading news...</p>;
if (cryptoList) {
return cryptoList.data.map((crypto) => <p> {crypto.id}</p>);
}
};
return (
<div className="container">
<div className="row">CryptoComponent: {renderCrypto()}</div>
</div>
);
};
export default CryptoComponent;
All constructed values from the state: cryptoList, loading, hasErrors, seem to be undefined at the component level.
Any suggestions are appreciated!
Have you tried using the following createSelector code:
export const selectCryptos = createSelector(
(state) => state,
(state) => ({
cryptoList: state.cryptoList,
loading: state.loading,
})
);
As per documentation state should be the first parameter:
https://redux.js.org/usage/deriving-data-selectors
I am creating a react/ redux app using json fake api server redux toolkit.
I am adding a some datas from and api to redux slice and trying to retrieve to my component. data seems empty and no error showing could anyone able to help me in this if possible.
my redux slice
import {
createAsyncThunk,
createSlice,
createSelector,
} from "#reduxjs/toolkit";
import axios from "axios";
import { base_emp } from "./api";
const initialState = {
emplist: [],
loading: "loading",
};
export const fetchEmployees = createAsyncThunk(
"employee/emplist",
async (_, thunkAPI) => {
try {
const response = await axios.get(base_emp);
return await response.json();
} catch (error) {
return thunkAPI.rejectWithValue({ error: error.message });
}
}
);
export const userSlice = createSlice({
name: "user",
initialState,
// The `reducers` field lets us define reducers and generate associated actions
reducers: {
decrement: (state) => {
state.value -= 1;
},
// Use the PayloadAction type to declare the contents of `action.payload`
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
extraReducers: (builder) => {
builder.addCase(fetchEmployees.pending, (state) => {
state.emplist = [];
state.loading = "loading";
});
builder.addCase(fetchEmployees.fulfilled, (state, { payload }) => {
state.emplist = payload;
state.loading = "loaded";
});
builder.addCase(fetchEmployees.rejected, (state, action) => {
state.loading = "error";
state.error = action.error.message;
});
},
});
export const { increment, decrement, incrementByAmount } = userSlice.actions;
export const selectCount = (state) => state.counter.value;
export const selectEmployees = createSelector(
(state) => ({
products: state.emplist,
loading: state.loading,
}),
(state) => state
);
export default userSlice.reducer;
my component goes here
import React, { useState, useEffect } from "react";
import EmployeeDetails from "./EmployeeDetails";
import { useSelector, useDispatch } from "react-redux";
import { fetchEmployees, selectEmployees } from "./features/auth/userSlice";
const Employelist = () => {
const [employe, setEmployee] = useState([]);
const dispatch = useDispatch();
const { emplist } = useSelector(selectEmployees);
React.useEffect(() => {
dispatch(fetchEmployees());
}, [dispatch]);
useEffect(() => {
emplist &&
emplist.then((res) => res.json()).then((data) => setEmployee(data));
}, []);
const handleclick = (id) => {
const emp = employe.filter((emp) => emp.employeeid !== id);
setEmployee(emp);
};
// console.log(currentUsers);
return (
<div>
<EmployeeDetails handleclick={handleclick} employe={employe} />
</div>
);
};
export default Employelist;
api.js
import axios from "axios";
export const base_emp = axios.get("http://localhost:5000/emp");
error message in console