State updates but not in component - reactjs

I am using redux-toolkit and I was trying to handle request errors. after trying console loging I found out that my error state updates but not when I need.
const ProductCreatePage = () => {
const {token} = useContext(UserContext);
const {error} = useSelector(state => state.ui)
const dispatch = useDispatch()
const navigate = useNavigate();
const createProductHandler = async (event) => {
event.preventDefault();
const form = document.querySelector('form');
const productData = {
price: Number.parseInt(event.target.price.value),
name: event.target.name.value,
status: event.target.status.value === "true" ? true : false
};
const formData = new FormData();
event.target.querySelectorAll("input").forEach(({ name, type, value, files, ...element }) => {
if (type === 'file') {
formData.append(`files.img`, files[0], files[0].name);
}
});
formData.append('data', JSON.stringify(productData));
await dispatch(createProduct({
productData: formData,
token
}))
console.log(error)
if(error === false){
// navigate(routes.products,{state:{create: true}})
console.log("sss")
}
}
return(...)
}
this is the function that creates product in redux using redux-toolkit
export const createProduct = ({productData,token}) => {
return async (dispatch) => {
try {
dispatch(ProductSliceAction.loadingToggle())
const {data} = await axios.post(`https://www.youtube.com/watch?v=xWpnTGmS8-Q`,productData,{
headers: {
Authorization: `Bearer ${token}`
}
})
dispatch(UiSliceAction.resetErrors())
dispatch(ProductSliceAction.loadingToggle())
}catch (error){
dispatch(UiSliceAction.setErrors({
message: error.message,
code: error.response.status
}))
dispatch(ProductSliceAction.loadingToggle())
}
}
}
and this is my error redux slice
const initialState = {
error: false
}
const UiSlice = createSlice({
name: "ui",
initialState,
reducers: {
setErrors: (state,{payload}) => {
state.error = payload;
},
resetErrors: (state) => {
state.error = initialState.error;
}
}
});
I want to handle errors like "Network Error" , 403 , ... and store the error in UiSlice error and for doing that I am using dispatch like below
dispatch(UiSliceAction.setErrors({
message: error.message,
code: error.response.status
}))
the state updates but this update not effecting code below
if(error === false){
// navigate(routes.products,{state:{create: true}})
console.log("sss")
}
I did multiple console.log and I found out state does not update in component (it updates in reducer when use console.log)
now I want to know where is the problem. why my state updates with delay(after exciting if(error === false))
this is the logs
enter image description here

Related

Redux toolkit Bearer token undefined

Error: authorization bearer undefined is shown in the browser on the .
Here is my
Here is my code of useEffect of OrderScreen.js Here I have tried to dispatch check if user have value or not if not redirect them to /login which is login page. I am redirecting to login page because from use state i am not getting any value.
const dispatch = useDispatch()
const navigate = useNavigate()
const { user } = useSelector((state) => state.auth)
const { orders, isLoading, isError, message } = useSelector(
(state) => state.orders,
)
useEffect(() => {
if (isError) {
toast.error(message)
}
if (!user && !user.isAdmin) {
navigate('/login')
}
dispatch(getOrder())
return () => {
dispatch(reset())
}
}, [user, isError, message, dispatch, navigate])
`
Here is my orderSlice. for the getOrder Function `
const initialState = {
orders: [],
isError: false,
isSuccess: false,
isLoading: false,
message: '',
}
export const getOrder = createAsyncThunk(
'orders/getOrder',
async (_, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token
return await orderService.getOrder(token)
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message)
}
},
)
`
Here is my orderService.js `
const getOrder = async (token) => {
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
}
const response = await axios.get(API_URL, config)
return response.data
}
`
I tried to solve from these stacks
Authorization Bearer Token Header in Javascript
How to send bearer token through header of axios call in react redux
You can try to pass the token as first argument of your thunk function ( currently "_") to make sure it is not undefined. Also, you can use a debugger to know the actual state of the global store (or simply log it in your component).
And lastly, try to log what returns the thunkApi.getState() function.

Redux toolkit action is stuck in pending state even after api response is recieved

I am using redux toolkit and all of the other actions for slice are working as expected but only editCountry action has problem. My editCountry action is stuck in country/editCountry/pending. I have no idea what is wrong with code.
This is how redux slice code is defined.
slice.js
const initialState = {
countries: {},
isLoading: false,
isError: false,
isSuccess: false,
message: ''
}
export const editCountry = createAsyncThunk(
'country/editCountry',
async (payload, thunkAPI) => {
try {
const response = await apiProvider.editCountry(payload);
return response;
}
catch (e) {
return thunkAPI.rejectWithValue(e)
}
})
const countriesSlice = createSlice({
name: 'country',
initialState,
reducers: { },
extraReducers: (builder) => {
builder
.addCase(editCountry.pending, (state, action) => {
state.isLoading = true
})
.addCase(editCountry.fulfilled, (state, action) => {
console.log('fulfilled before: ', state); // shown on console
console.log('fulfilled action: ', action.payload.data); // undefined
const unUpdatedCountries = state.countries.data.filter(c => c._id !==
action.payload.data._id)
const updatedCountries = [action.payload.data, ...unUpdatedCountries];
state.isLoading = false;
state.isSuccess = true;
state.countries = {...state.countries, data: updatedCountries };
console.log('fulfilled after: ', state); // do not shown on console
})
.addCase(editCountry.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
}
Here is the Api helper method which is invoked inside editCountry action.
ApiProvider.js
export const editCountry = async (payload) => {
let token = getToken();
const config = {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
Accept: "application/json",
},
};
try{
const {data} = await axios.put(`${baseUrl}/edit-country`, payload, config);
if(data.status){
return Promise.resolve(data);
}
return Promise.reject(data.message);
}
catch (e) {
return Promise.reject(e.message);
}
}
And here is my Submit method from where action is dispatched.
Country.js
const handleSubmit = async (e, newFormData) => {
e.preventDefault()
const apiPayload = {
Country_ID: newFormData.id,
Name: newFormData.name,
DialCode: newFormData.dialCode.includes("+") ? newFormData.dialCode : "+"+newFormData.dialCode,
ISO: newFormData.iso,
CallRate: newFormData.minutePrice,
Blocked: newFormData.isBlocked.value,
}
try {
await dispatch(editCountry(apiPayload)).unwrap()
toggle() // hide form modal
toast.success('Country updated successfully');
}
catch (e) {
setFormData({
...formData,
errorMsg: e.message,
});
toggle();
toast.error(e);
}
}
On action dispatch redux is stuck on pending but in network tab api call is completed.
[enter image description here][1]
Network tab image:
[1]: [https://i.stack.imgur.com/X3rbA.png][1]
Redux dev tools image:
[1]: https://i.stack.imgur.com/sQUdJ.png
Just return the data, don't return wrap it in Promise.resolve.
if(data.status){
return Promise.resolve(data);
}
return Promise.reject(data.message);
}
In editCountry.fulfilled, it should be action.payload not action.payload.data.
In editCountry, you send data and in editCountry async thunk, you send the complete response which is basically the data. In editCountry.fulfilled, you have complete data in payload but you are trying to access the data from payload.
Hope this solve your issue

redux-toolkit delete all items when i try to delete one

i'm migrating slowly to redux-toolkit
when i try to delete a single item from my store, the action works well because i send the right ID from the component, but i think that the way i send this id to the slice is incorrect so i'm not able to delete the right item from my store(it delete all the items)
component dispatch code:
const handleDelete = async (e) => {
e.preventDefault()
const del_id = e.currentTarget.getAttribute("data-id")
dispatch(deleteComment(del_id))
}
action & api call:
export const deleteComment = (id) => async (dispatch, getState) => {
try {
dispatch(deleteCommentsLoading())
const {
userLogin: { userInfo },
} = getState()
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${userInfo.token}`,
},
}
const { data } = await axios.delete(
`${process.env.REACT_APP_API_KEY}/publication/comment/delete/${id}`,
config
)
dispatch(deleteCommentsSuccess({ commentId: id }))
// i tried deleteCommentsSuccess(data); (id)... nothing work to send this id to the slice
} catch (error) {
dispatch(
deleteCommentsFail(
error.response && error.response.data.message
? error.response.data.message
: error.message
)
)
}
}
my slice:
const commentSlice = createSlice({
name: "comments",
initialState: {
comment: {},
loading: false,
error: false,
},
... other reducers
// DELETE COMMENT
deleteCommentsLoading: (state) => {
state.loading = true
},
deleteCommentsFail: (state, action) => {
state.error = action.payload
state.loading = false
},
deleteCommentsSuccess: (state, action) => {
const { commentId } = action.payload
state.comment.comments.filter((item) => item._id !== commentId)
// i tried first when i send data or id to put action.payload.id nothing work
state.loading = false
state.error = false
},
and this's my comments store slice, every item have his own "_id"
How is your backend. (comment)
Something like this? :
const receita = await Recipe.findById(req.params.id)
if (receita) {
await receita.remove()
res.json({ message: 'Receita removed' })
} else {
res.status(404)
throw new Error('Receita not found')
}
})
state.comment.comments.filter((item) => item._id !== commentId) will just return a filtered copy of the array,but not actually modify anything.
You have to do
state.comment.comments = state.comment.comments.filter((item) => item._id !== commentId)

Access to API using Redux

I have a react-redux app. I need to call API and used it in my component. The app is called with fetch in function in utills.
All functions are group and export like this:
export const sportTeam = {
getBasketballTeam,
getBasketballTeamById,
}
function getBasketballTeam() {
let token = store.getState().UserReducer.token;
fetch(
actions.GET_BASKETBALLTEAM,
{
method: "GET",
headers: { Authorization: `Bearer ${token}` },
}
)
.then((res) => {
if (res.status == 200 ) {
return res.json();
}
})
.then((response) => {
console.log(response);
})
.catch((err) => {
console.log(err);
});
}
getBasketballTeam contains an array of objects.
How can I get getBasketballTeam and used it in the component in the view to returning the list with this data?
You don't want your getBasketballTeam function to access the store directly through store.getState().
What you want is a "thunk" action creator that gets the store instance as an argument when you dispatch it.
The flow that you want is this:
Component continuously listens to the basketball team state with useSelector (or connect).
Component mounts.
Component dispatches a getBasketballTeam action.
Action fetches data from the API.
Reducer saves data from the action to the state.
State updates.
Component re-renders with the new data from state.
The easiest way to do this is with the createAsyncThunk function from Redux Toolkit. This helper handles all errors by dispatching a separate error action. Try something like this:
Action:
export const fetchBasketballTeam = createAsyncThunk(
"team/fetchBasketballTeam",
async (_, thunkAPI) => {
const token = thunkAPI.getState().user.token;
if ( ! token ) {
throw new Error("Missing access token.");
}
const res = await fetch(actions.GET_BASKETBALLTEAM, {
method: "GET",
headers: { Authorization: `Bearer ${token}` }
});
if (res.status !== 200) {
throw new Error("Invalid response");
}
// what you return is the payload of the fulfilled action
return res.json();
}
);
Reducer:
const initialState = {
status: "idle",
data: null
};
export const teamReducer = createReducer(initialState, (builder) =>
builder
.addCase(fetchBasketballTeam.pending, (state) => {
state.status = "pending";
})
.addCase(fetchBasketballTeam.fulfilled, (state, action) => {
state.status = "fulfilled";
delete state.error;
state.data = action.payload;
})
.addCase(fetchBasketballTeam.rejected, (state, action) => {
state.status = "rejected";
state.error = action.error;
})
);
Store:
export const store = configureStore({
reducer: {
team: teamReducer,
user: userReducer,
}
});
Component:
export const BasketballTeam = () => {
const { data, error, status } = useSelector((state) => state.team);
const dispatch = useDispatch();
useEffect(
() => {
dispatch(fetchBasketballTeam());
},
// run once on mount
// or better: take the token as an argument and re-run if token changes
[dispatch]
);
if (status === "pending") {
return <SomeLoadingComponent />;
}
if (!data) {
return <SomeErrorComponent />;
}
// if we are here then we definitely have data
return <div>{/* do something with data */}</div>;
};
After you get response you need to do the following things
call dispatch function to store the data received in REDUX state.
Now when you have data in redux state, you can use useSelector() to get that state and make use of it in your jsx file.

Dispatch multiples http request React/Redux

I'm trying to dispatch more than one axios request inside my method. However, it is not working.
export const getImages = (res) => {
return {
type: actionTypes.GET_IMAGES,
payload: res
}
}
export const loadImages = (imgs, cId) => {
return dispatch => {
let data = [];
for(const i of imgs) {
const id = i.id;
axios.get(`${api.URL}/test/${cId}/files/${id}`)
.then(res => {
if(res.data !== -1) {
const obj = {
name: res.data,
desc: i.caption
};
data(obj);
}
//dispatch(getImages(data));
});
}
console.log('Action:');
console.log(data);
dispatch(getImages(data));
}
}
The console log does not print anything. Do I need to dispatch inside the .then()? If so, how can I run multiples requests before dispatching?
Thanks

Resources