T
TanStack3y ago
xenial-black

I'm working with optimistic updates and data gets mutated fine but not reverted when an error occurs

Hey there, I was following this documentation to get optimistic updates working in my project. The full_name field in my profile object gets updated correctly when the user submits the form. But when I throw the error on purpose the data that got updated does not revert back. https://tanstack.com/query/v4/docs/react/guides/optimistic-updates
export const useUpdateName = (newName: string, onSuccess?: () => void) => {
const queryClient = useQueryClient();
const { triggerSuccessHaptic } = useHaptics();

const key = ['update-profile-name', newName];

const { data: userProfile } = useGetUserSession();
const userId = userProfile?.user?.id;

return useMutation(
key,
() => updateProfileName(userId, newName).then(result => result),
{
onMutate: async () => {
await queryClient.cancelQueries({
queryKey: ['get-user-profile', userId],
});

const previousProfile = queryClient.getQueryData([
'get-user-profile',
userId,
]);

queryClient.setQueryData(
['get-user-profile', userId],
(oldData: UserProfileType) => {
oldData.full_name = newName;

return oldData;
},
);

return { previousProfile };
},
onSuccess: () => {
triggerSuccessHaptic();

if (onSuccess) {
onSuccess();
}
},
onError: (error, _, context) => {
queryClient.setQueryData(
['get-user-profile', userId],
context.previousProfile,
);
console.log(error);
},
},
);
};

const updateProfileName = async (userID: string, newName: string) => {
const { error } = await supabaseClient
.from('profiles')
.update({ full_name: newName })
.eq('id', userID)
.select()
.single();

if (error) {
throw Error(error.message);
}

return true;
};
export const useUpdateName = (newName: string, onSuccess?: () => void) => {
const queryClient = useQueryClient();
const { triggerSuccessHaptic } = useHaptics();

const key = ['update-profile-name', newName];

const { data: userProfile } = useGetUserSession();
const userId = userProfile?.user?.id;

return useMutation(
key,
() => updateProfileName(userId, newName).then(result => result),
{
onMutate: async () => {
await queryClient.cancelQueries({
queryKey: ['get-user-profile', userId],
});

const previousProfile = queryClient.getQueryData([
'get-user-profile',
userId,
]);

queryClient.setQueryData(
['get-user-profile', userId],
(oldData: UserProfileType) => {
oldData.full_name = newName;

return oldData;
},
);

return { previousProfile };
},
onSuccess: () => {
triggerSuccessHaptic();

if (onSuccess) {
onSuccess();
}
},
onError: (error, _, context) => {
queryClient.setQueryData(
['get-user-profile', userId],
context.previousProfile,
);
console.log(error);
},
},
);
};

const updateProfileName = async (userID: string, newName: string) => {
const { error } = await supabaseClient
.from('profiles')
.update({ full_name: newName })
.eq('id', userID)
.select()
.single();

if (error) {
throw Error(error.message);
}

return true;
};
Optimistic Updates | TanStack Query Docs
When you optimistically update your state before performing a mutation, there is a chance that the mutation will fail. In most of these failure cases, you can just trigger a refetch for your optimistic queries to revert them to their true server state. In some circumstances though, refetching may not work correctly and the mutation error could ...
2 Replies
like-gold
like-gold3y ago
Is the console.log(error) in onError called? If you also console.log userId and context inside onError, do you get the expected values?
xenial-black
xenial-blackOP3y ago
Not sure why I didn't do that before but yeah I see that the context.previousProfile actually has the mutated version not the original version. Hmm. Silly mistake, I was mutating the reference to the previousProfile so that's why onError it was always going to be that new value. I had to create a copy of that previous value. Figured it out though!

Did you find this page helpful?