Blog>
Snippets

Rollback optimistic update on mutation error

Show how to revert optimistic updates when a mutation fails by using the onError callback within the useMutation hook.
const { mutate } = useMutation(updateTodo, {
  onMutate: async (todo) => {
    // Cancel any outgoing refetches (so they don't overwrite our optimistic update)
    await queryClient.cancelQueries('todos');
    
    // Snapshot the previous value
    const previousTodos = queryClient.getQueryData('todos');
    
    // Optimistically update to the new value
    queryClient.setQueryData('todos', old => old.map(t => t.id === todo.id ? { ...t, ...todo } : t));
    
    // Return a context with the previous and new todo for potential rollback
    return { previousTodos };
  },
  onError: (err, todo, context) => {
    // Rollback to the previous value
    queryClient.setQueryData('todos', context.previousTodos);
  },
  onSettled: () => {
    // Refetch after success or failure
    queryClient.invalidateQueries('todos');
  },
});
This code snippet shows how to implement an optimistic update with a rollback on error using React Query's useMutation hook. When the `updateTodo` mutation is initiated, `onMutate` snaps a picture of the current todos and optimistically updates the UI. If the mutation encounters an error, `onError` rolls back the UI to its previous state using the snapshot taken earlier. Finally, `onSettled` ensures that the data is refetched from the server regardless of the mutation's outcome.