Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Learn Optimistic Updates | React Query in Practice
Practice
Projects
Quizzes & Challenges
Quizzes
Challenges
/
React Query Essentials

bookOptimistic Updates

Optimistic updates are a powerful technique that can make your applications feel faster and more responsive. When you use optimistic updates, you immediately update the UI as if a mutationβ€”such as editing or deleting an itemβ€”has already succeeded, even before you receive a response from the server. This approach gives users instant feedback, reducing perceived latency and making the interface feel snappier.

To implement optimistic updates in React Query, you use the useMutation hook along with its lifecycle callbacks. The most important callbacks for optimistic updates are onMutate, onError, and onSettled. The onMutate callback is triggered before the mutation function runs. Here, you can update the cache to reflect the anticipated change. If the mutation fails, the onError callback lets you roll back the cache to its previous state. Finally, onSettled ensures that your cache stays in sync with the server by refetching or updating the relevant queries.

Suppose you have a todo list and want to let users mark a todo as completed. With optimistic updates, you update the todo's status in the UI as soon as the user clicks, without waiting for the server. If the server later reports an error, you revert the change.

Here is a step-by-step example of an optimistic update flow:

  • When a user marks a todo as completed, you trigger a mutation using useMutation;
  • In the onMutate callback, you update the cached todo list so the UI reflects the change immediately;
  • If the mutation fails, the onError callback restores the previous cache state, rolling back the UI;
  • Once the server responds (success or failure), the onSettled callback ensures the cache is up to date.
import { useMutation, useQueryClient } from '@tanstack/react-query';

const queryClient = useQueryClient();

const mutation = useMutation({
  mutationFn: updateTodoOnServer,

  // Optimistically update the cache before the mutation function runs
  onMutate: async (updatedTodo) => {
    await queryClient.cancelQueries({ queryKey: ['todos'] });

    const previousTodos = queryClient.getQueryData(['todos']);

    queryClient.setQueryData(['todos'], (old = []) =>
      old.map((todo) =>
        todo.id === updatedTodo.id
          ? { ...todo, completed: updatedTodo.completed }
          : todo
      )
    );

    return { previousTodos };
  },

  // Roll back the cache if the mutation fails
  onError: (error, updatedTodo, context) => {
    if (context?.previousTodos) {
      queryClient.setQueryData(['todos'], context.previousTodos);
    }
  },

  // Ensure the cache stays in sync with the server
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['todos'] });
  },
});

With this setup, users see the todo marked as completed right away. If the server rejects the update, the UI reverts to its previous state, maintaining consistency between the client and the server.

question mark

Which of the following is the main benefit of using optimistic updates in your application?

Select the correct answer

Everything was clear?

How can we improve it?

Thanks for your feedback!

SectionΒ 4. ChapterΒ 2

Ask AI

expand

Ask AI

ChatGPT

Ask anything or try one of the suggested questions to begin our chat

bookOptimistic Updates

Swipe to show menu

Optimistic updates are a powerful technique that can make your applications feel faster and more responsive. When you use optimistic updates, you immediately update the UI as if a mutationβ€”such as editing or deleting an itemβ€”has already succeeded, even before you receive a response from the server. This approach gives users instant feedback, reducing perceived latency and making the interface feel snappier.

To implement optimistic updates in React Query, you use the useMutation hook along with its lifecycle callbacks. The most important callbacks for optimistic updates are onMutate, onError, and onSettled. The onMutate callback is triggered before the mutation function runs. Here, you can update the cache to reflect the anticipated change. If the mutation fails, the onError callback lets you roll back the cache to its previous state. Finally, onSettled ensures that your cache stays in sync with the server by refetching or updating the relevant queries.

Suppose you have a todo list and want to let users mark a todo as completed. With optimistic updates, you update the todo's status in the UI as soon as the user clicks, without waiting for the server. If the server later reports an error, you revert the change.

Here is a step-by-step example of an optimistic update flow:

  • When a user marks a todo as completed, you trigger a mutation using useMutation;
  • In the onMutate callback, you update the cached todo list so the UI reflects the change immediately;
  • If the mutation fails, the onError callback restores the previous cache state, rolling back the UI;
  • Once the server responds (success or failure), the onSettled callback ensures the cache is up to date.
import { useMutation, useQueryClient } from '@tanstack/react-query';

const queryClient = useQueryClient();

const mutation = useMutation({
  mutationFn: updateTodoOnServer,

  // Optimistically update the cache before the mutation function runs
  onMutate: async (updatedTodo) => {
    await queryClient.cancelQueries({ queryKey: ['todos'] });

    const previousTodos = queryClient.getQueryData(['todos']);

    queryClient.setQueryData(['todos'], (old = []) =>
      old.map((todo) =>
        todo.id === updatedTodo.id
          ? { ...todo, completed: updatedTodo.completed }
          : todo
      )
    );

    return { previousTodos };
  },

  // Roll back the cache if the mutation fails
  onError: (error, updatedTodo, context) => {
    if (context?.previousTodos) {
      queryClient.setQueryData(['todos'], context.previousTodos);
    }
  },

  // Ensure the cache stays in sync with the server
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['todos'] });
  },
});

With this setup, users see the todo marked as completed right away. If the server rejects the update, the UI reverts to its previous state, maintaining consistency between the client and the server.

question mark

Which of the following is the main benefit of using optimistic updates in your application?

Select the correct answer

Everything was clear?

How can we improve it?

Thanks for your feedback!

SectionΒ 4. ChapterΒ 2
some-alt