T
TanStack13mo ago
harsh-harlequin

Global error handler for mutations and meta

Hello ! I would like to know what is the best way of passing down data to a global handler ? I checked meta and it seems almost too simple, it is better to use it or instead leverage the onMutate to provide context ? With onMutate, if you want to use the context in the onError handler, it is harder to use it with typescript than meta because of the unknown type My context is that I have a legacy "snackbar" wrapper for useMutation, and by default, it displays a snackbar for success / errors, but we can disable the snackbars for the errors if we want. This is currently leveraged "per mutation" on the onError / onSuccess handlers, and I would like to move it to a global handler instead.
1 Reply
harsh-harlequin
harsh-harlequinOP13mo ago
Code of my snackbar wrapper below
export interface SnackbarOptions<TData = unknown, TVariables = unknown> {
successSnackbarMessage?: (data: TData, variables: TVariables) => string;
showSnackbarOnError?: boolean;
errorSnackbarMessage?: string;
}

const defaultSnackbarOptions: SnackbarOptions = {
showSnackbarOnError: true
};

const useSnackbarMutation = <TData = unknown, TError = unknown, TVariables = void>(
mutationKey: MutationKey,
mutationFn: MutationFunction<TData, TVariables>,
options?: Omit<UseMutationOptions<TData, TError, TVariables, SnackbarOptions<TData, TVariables>>, "mutationFn">,
snackbarOptions: SnackbarOptions<TData, TVariables> = {} // Show snackbar on error by default
): UseMutationResult<TData, TError, TVariables, SnackbarOptions<TData, TVariables>> => {
const { enqueueSnackbar } = useSnackbar();
const { successSnackbarMessage, showSnackbarOnError, errorSnackbarMessage } = {
...defaultSnackbarOptions,
...snackbarOptions
};

return useMutation<TData, TError, TVariables, SnackbarOptions<TData, TVariables>>(mutationKey, mutationFn, {
...options,
meta: {
...defaultSnackbarOptions,
...snackbarOptions
},
onMutate: () => {
return {
...defaultSnackbarOptions,
...snackbarOptions
};
},
onSuccess: (data, variables, context) => {
if (successSnackbarMessage) {
// ...some success handling
}
if (options?.onSuccess) {
options.onSuccess(data, variables, context);
}
},
onError: (error, variables, context) => {
if (showSnackbarOnError) {
// ...error handling here
}
}
});
};
export interface SnackbarOptions<TData = unknown, TVariables = unknown> {
successSnackbarMessage?: (data: TData, variables: TVariables) => string;
showSnackbarOnError?: boolean;
errorSnackbarMessage?: string;
}

const defaultSnackbarOptions: SnackbarOptions = {
showSnackbarOnError: true
};

const useSnackbarMutation = <TData = unknown, TError = unknown, TVariables = void>(
mutationKey: MutationKey,
mutationFn: MutationFunction<TData, TVariables>,
options?: Omit<UseMutationOptions<TData, TError, TVariables, SnackbarOptions<TData, TVariables>>, "mutationFn">,
snackbarOptions: SnackbarOptions<TData, TVariables> = {} // Show snackbar on error by default
): UseMutationResult<TData, TError, TVariables, SnackbarOptions<TData, TVariables>> => {
const { enqueueSnackbar } = useSnackbar();
const { successSnackbarMessage, showSnackbarOnError, errorSnackbarMessage } = {
...defaultSnackbarOptions,
...snackbarOptions
};

return useMutation<TData, TError, TVariables, SnackbarOptions<TData, TVariables>>(mutationKey, mutationFn, {
...options,
meta: {
...defaultSnackbarOptions,
...snackbarOptions
},
onMutate: () => {
return {
...defaultSnackbarOptions,
...snackbarOptions
};
},
onSuccess: (data, variables, context) => {
if (successSnackbarMessage) {
// ...some success handling
}
if (options?.onSuccess) {
options.onSuccess(data, variables, context);
}
},
onError: (error, variables, context) => {
if (showSnackbarOnError) {
// ...error handling here
}
}
});
};
^ the context types were just added to test the onMutate function; it was on unknown previously

Did you find this page helpful?