T
TanStack3mo ago
other-emerald

default mutation onError fallback?

I would like to create a global useMutation error fallback, where it runs only if the specific mutation doesn't have its own onError handler, or if that error throws. I have set defaultOptions in QueryClient as so:
const queryClient = new QueryClient({
defaultOptions: {
mutations: {
onError: (error) => {
console.error("Error:", error);
},
},
},
});
const queryClient = new QueryClient({
defaultOptions: {
mutations: {
onError: (error) => {
console.error("Error:", error);
},
},
},
});
having this mutation:
const addMutation = useMutation(
trpc.iot.addNotificationContact.mutationOptions()
);
addMutation.mutate(res, {
onError: (error) => {
console.error("Failed to add notification contacts:", error);
},
});
const addMutation = useMutation(
trpc.iot.addNotificationContact.mutationOptions()
);
addMutation.mutate(res, {
onError: (error) => {
console.error("Failed to add notification contacts:", error);
},
});
both onError handlers are running. I would like addMutation to be the only one running.
4 Replies
adverse-sapphire
adverse-sapphire3mo ago
You should be able to use meta on your useMutation and access it in onError:
useMutation({
meta: {
// Named whatever you want
muted: true
},
...
})

...

onError: (error, variables, context, mutation) => {
if (!mutation.meta.muted) {
// ... error
}
}
useMutation({
meta: {
// Named whatever you want
muted: true
},
...
})

...

onError: (error, variables, context, mutation) => {
if (!mutation.meta.muted) {
// ... error
}
}
Not sure why this is, but you need to use MutationCache for this approach to work. Not defaultOptions.mutations I guess mutation.options.onError exists too so that'd be better to use.
onError: (error, variables, context, mutation) => {
if (!mutation.options.onError) {
// ... error
}
}
onError: (error, variables, context, mutation) => {
if (!mutation.options.onError) {
// ... error
}
}
other-emerald
other-emeraldOP3mo ago
that only works when onError is declared on mutation initialization unfortunately, not when it's declared during mutation.mutate
rival-black
rival-black3mo ago
I believe that it is intended that onError within the useMutation call is designed to override the queryClient default, while onError from the mutate function is meant to "extend" the onError. While I haven't personally encountered this, a super simple solution is to just to export your default onError function as some kind of wrapper around onError and use it manually. For example, you could do something like
const myMutation = useMutation({
trpc.iot.addNotificationContact.mutationOptions(),
onError: fallbackDefaultErrorHandler((error, variables, context, mutation) => {
// local code here
})
});
const myMutation = useMutation({
trpc.iot.addNotificationContact.mutationOptions(),
onError: fallbackDefaultErrorHandler((error, variables, context, mutation) => {
// local code here
})
});
Then you could simply have your fallbackDefaultErrorHandler be a higher order function that wraps your code in a try catch and invokes the default your code crashes. --- Just from my personal experience, I haven't experienced a situation where I actually need to define onError on mutate itself if my goal is to override the behaviour of the default onError - what problem are you trying to solve with having both options available to you?
compatible-crimson
compatible-crimson3mo ago
in OPs example, different onError handlers are used: The one on .mutate is different than the one on useMutation, and the default only sets the one for useMutation. simply put, the one on .mutate will always run if the component is still mounted when the mutation finishes. if you want "a default only if the component doesn't override it", set the default like you did, but then use the callback on useMutation, as this one overwrites the default.

Did you find this page helpful?