React Query Mutation not working as expected
Answered
Korat posted this in #help-forum
KoratOP
Hey guys, I have this functionality to add a product in cart in a client side component.
I wanted to extract that logic into a custom hook called useCart.
The thing is that in client component, react query executes mutations onSuccess but inside the hook it doesnt.
I would love to get some feedback from you.
Here is my useCart functionality.
Note: the same code works in the client side component
I wanted to extract that logic into a custom hook called useCart.
The thing is that in client component, react query executes mutations onSuccess but inside the hook it doesnt.
I would love to get some feedback from you.
Here is my useCart functionality.
export const useCart = (article: Article) => {
/**
* destructures
*/
const {
articleId,
name,
coverImagePath,
articleSizes,
articleColors,
unitPrice,
} = article;
const { status } = useSession();
const addToCart = useCartStore((state) => state.addToCart);
const cartArticles = useCartStore((state) => state.cart.shoppingCartArticles);
/**
* mutations
*/
const { mutate } = useMutation({
mutationFn: addItemToCartMutation,
onSuccess: () => {
console.log('Hello WOrld');
toast({
title: 'Great!',
description: 'Your product was added to cart.',
});
},
});
const addToCartHandler = () => {
if (status === 'authenticated') mutate(articleId);
else {
const item = cartArticles.find((item) => item.itemId === articleId);
if (item) {
toast({
title: 'Opss!',
description: 'This product is already in cart.',
});
return;
}
addToCart({
itemId: articleId,
name,
coverImagePath,
unitTypeName: articleSizes.find((size) => size.selectedSize)
?.unitTypeName as string,
unitTypeSize: articleSizes.find((size) => size.selectedSize)
?.unitTypeSize as string,
colorName: articleColors.find((color) => color.selectedColor)
?.colorName as string,
unitPrice,
quantity: 1,
});
toast({
title: 'Great!',
description: 'Your product was added to cart.',
});
}
};
return { addToCartHandler };
};Note: the same code works in the client side component
Answered by Korat
Yeah, I have some actions using axios and im planning to entirely get rid of axios and move to fetch.
And the way I configured mutation cache -> i was destructuring message which works fine for mutations that use axios but the destructure fails for those using fetch, thats why onSuccess was never being called.
Thank you 🙂
And the way I configured mutation cache -> i was destructuring message which works fine for mutations that use axios but the destructure fails for those using fetch, thats why onSuccess was never being called.
Thank you 🙂
55 Replies
KoratOP
Also my mutationFn consist of a function which looks like this ->
'use server';
import { revalidateTag } from 'next/cache';
import api from '@/initializations/fetch';
/**
*
* @param articleId
* @returns
*/
export const addItemToCartMutation = async (articleId: number) => {
await api.post(`shop/add-to-shopping-cart/${articleId}`);
revalidateTag('shopping-cart');
};KoratOP
Apparently there was some issues with how I configured React Queries Mutation Cache, thats why the onSuccess wasn't being executed 🙂
@Korat Apparently there was some issues with how I configured React Queries Mutation Cache, thats why the onSuccess wasn't being executed 🙂
so, your problem is solved? (if so 🎉 , otherwise 😟)
KoratOP
Yeah, I have some actions using axios and im planning to entirely get rid of axios and move to fetch.
And the way I configured mutation cache -> i was destructuring message which works fine for mutations that use axios but the destructure fails for those using fetch, thats why onSuccess was never being called.
Thank you 🙂
And the way I configured mutation cache -> i was destructuring message which works fine for mutations that use axios but the destructure fails for those using fetch, thats why onSuccess was never being called.
Thank you 🙂
Answer
yeah good idea to use fetch: https://www.adios-axios.com/
@Korat Yeah, I have some actions using axios and im planning to entirely get rid of axios and move to fetch.
And the way I configured mutation cache -> i was destructuring message which works fine for mutations that use axios but the destructure fails for those using fetch, thats why onSuccess was never being called.
Thank you 🙂
i mean, i did nothing to help, but glad to see you worked it out (also, tell me if i should have chosen another message)
KoratOP
Just the fact i know there are people here who I can ask for help motivates me and makes me feel nice.
Also do you have any resource how would fetch be best abstracted to handle authentication tokens from sessions and other stuff ?
Also do you have any resource how would fetch be best abstracted to handle authentication tokens from sessions and other stuff ?
@riský i mean, i did nothing to help, but glad to see you worked it out (also, tell me if i should have chosen another message)
KoratOP
What message are you talking about 😆
Original message was deleted
^ marking it as solved
KoratOP
No its all fine brother, thank you
@Korat Just the fact i know there are people here who I can ask for help motivates me and makes me feel nice.
Also do you have any resource how would fetch be best abstracted to handle authentication tokens from sessions and other stuff ?
yeah nice, i was looking at this, and i wasn't sure... but then i saw you solved it and got happy 🙂
KoratOP
Sometimes the issue is far from the code provided haha, scaryyy
Western yellowjacket
@Korat how do you import a server function (
addItemToCartMutation) to a client component (useCart) ? Is that working?KoratOP
@Western yellowjacket Yeah im happy to tell you it works 🙂
Ofc im talking using react query
Western yellowjacket
My confusion is, how can you use a
server action outside a form... In this case you are passing addItemToCartMutation to a react query mutation.KoratOP
@Western yellowjacket Yeah, basically you create a file and mark the whole file as 'use server' and you can use the functions inside of it either in server components or in client components (passing it to react query's useMutation in this case).
Western yellowjacket
Yeah, I can see that now. How are you doing when your API returns an error? OnError of react query is invoked when throwing, but I think we can't do that in a server action, it will throw a 500
KoratOP
I think we can use try and catch to catch the error and return it ? , not sure with errors yet but the ones that come from a custom api are handled just fine in onSuccess
let me know if you find that out alright
KoratOP
We could handle that by wrapping the code in a try catch in that case onError will fire but not sure if you can take the error message from that
export const joinNewsletterFn = async (payload: Email) => {
try {
throw new Error('Hello World');
return await api.post('home/join-newsletter', {
body: payload,
});
} catch (error) {
throw error;
}
};const submitFormHandler = (payload: Email) =>
mutate(payload, {
onSuccess: () => {
console.log('Success!!!');
},
onError: (err) => {
console.log('Error!!!', err);
},
});onError will get triggered in this case but ofc the err doesnt contain any valuable information i think
@Western yellowjacket I think what you mentioned was the errors from api, in that case it was a success request/response so you can handle that in onSuccess, this is how i do it globaly
mutationCache: new MutationCache({
onSuccess: (data) => {
const { successMessage, errors } = data as Response<null>;
if (!errors)
toast({
description: successMessage || 'Action Successfully!',
});
else
errors?.forEach((error) =>
toast({
variant: 'destructive',
description: error.message,
})
);
},
}),
})Western yellowjacket
yeah, thats the problem... I was trying to get
onError from react-query to be invoked passing a error message, but I think that server actions will not permit this yet...Nile Crocodile
@California pilchard
California pilchard
@Korat can you share how your react query configuration looks like please ?!
@riský yeah good idea to use fetch: https://www.adios-axios.com/
where do you find such cool articles?
@California pilchard <@715218825863364679> can you share how your react query configuration looks like please ?!
KoratOP
Its set up just like docs suggest it
'use client';
import { PropsWithChildren, useState } from 'react';
import {
QueryClient,
QueryClientProvider as TanstackQueryClientProvider,
} from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
export default function QueryClientProvider({ children }: PropsWithChildren) {
const [queryClient] = useState(() => new QueryClient({}));
return (
<TanstackQueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</TanstackQueryClientProvider>
);
}California pilchard
const [queryClient] = useState(() => new QueryClient({})); why in useState ?KoratOP
@California pilchard They don't mention the reason in their docs, but you can check it out yourself at https://tanstack.com/query/latest/docs/react/guides/advanced-ssr
@Korat Yeah, I have some actions using axios and im planning to entirely get rid of axios and move to fetch.
And the way I configured mutation cache -> i was destructuring message which works fine for mutations that use axios but the destructure fails for those using fetch, thats why onSuccess was never being called.
Thank you 🙂
I'm looking at a similar transition from axios to fetch. How are you handling authentication? Do you have a wrapped version of Fetch setting headers etc...? Curious how you have things structured
KoratOP
@Matt Yes indeed, I have created a wrapper over fetch that handles sending token to the headers and other stuff, one thing I haven't had the chance to check yet is handling the language as well, if language is set to en, i should be using that language when calling our backend endpoints.
California pilchard
@Korat thanks a lot
KoratOP
@California pilchard No problem man, also one thing I have noticed with the current configuration used in their docs is that setting the staleTime to some number will cause some issues with syncing the cache when refetching the data so in that case I have removed it completly.
California pilchard
i wiil use react query only for mutaiton
for fetching i will use fetch
KoratOP
You will be using fetch with react query mutation aswell ?
California pilchard
yes using server actions
KoratOP
also good point, with next14 we better do mutations without react query
California pilchard
try {
const isCreated = await fetch(
`${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/api/v1/register`,
{
method: "POST",
body: JSON.stringify({
email: formData.email,
password: formData.email,
}),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
}
);
const data = await isCreated.json();
if (isCreated.ok) return data;
throw new Error(data.message);
} catch (error) {
console.log(error);
throw error;
}
}export default function useSignUpMutation() {
const { toast } = useToast();
return useMutation({
mutationFn: SingUpAction,
onSuccess: (data) => {
console.log({ success: data });
},
onError: (error) => {
console.log({ error: error });
toast({
variant: "destructive",
description: error.message.split(":")[1],
});
},
});
}KoratOP
Thats nice, but one question, how will you revalidate the data in this case ?
since you are doing the fetch in the server
lets say you have a list of users
you are saying that you will fetch them in server
and delete user (e.x) with react query
how would you revalidate users ?
California pilchard
in the server action that you use to delete the users you can then revalidate
KoratOP
okay great
California pilchard
its jsut like react query
KoratOP
yeah revalidatetag in server action
California pilchard
yes
@oxmaster where do you find such cool articles?
well, that one was written by the author of RQ and has been shared many times for questions about server components