Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Leer Optimistic Updates | Mutations and Updating Data
Practice
Projects
Quizzes & Challenges
Quizzes
Challenges
/
TanStack Query Server State Management in React

bookOptimistic Updates

Optimistic updates are a powerful technique that can make your React applications feel faster and more responsive. With optimistic updates, you update the UI immediately—before the server confirms that the mutation has succeeded. This approach gives users instant feedback for their actions, such as deleting an item or submitting a form, rather than waiting for a round-trip to the server.

The main benefit is a smoother user experience, especially for operations that might take a while to complete on the backend. Optimistic updates are especially useful in scenarios where you expect the mutation to succeed most of the time, and you want to minimize perceived latency in your application.

Suppose you have a list of users and want to let users delete entries from the list. With optimistic updates, you can remove the user from the UI right away, while the actual deletion request is still being processed by the server. Here is a simplified example of how you might implement this using TanStack Query's useMutation hook and the onMutate, onError, and onSettled callbacks:

import { useMutation, useQueryClient } from "@tanstack/react-query";

function UsersList({ users }) {
  const queryClient = useQueryClient();

  const deleteUserMutation = useMutation({
    mutationFn: (userId) =>
      fetch(`/api/users/${userId}`, { method: "DELETE" }),

    // Optimistically update the cache before the mutation happens
    onMutate: async (userId) => {
      await queryClient.cancelQueries({ queryKey: ["users"] });

      const previousUsers = queryClient.getQueryData(["users"]);

      queryClient.setQueryData(["users"], (old = []) =>
        old.filter((user) => user.id !== userId)
      );

      return { previousUsers };
    },

    // Roll back if the mutation fails
    onError: (err, userId, context) => {
      queryClient.setQueryData(["users"], context.previousUsers);
    },

    // Always refetch after error or success
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ["users"] });
    },
  });

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>
          {user.name}
          <button onClick={() => deleteUserMutation.mutate(user.id)}>
            Delete
          </button>
        </li>
      ))}
    </ul>
  );
}

In this example, when you click the Delete button, the user is immediately removed from the list in the UI. If the server deletion fails, the previous state is restored, ensuring data consistency.

Even with optimistic updates, there is always a chance that a mutation might fail—perhaps due to a network error or a server-side validation issue. In these situations, you need a strategy to roll back the optimistic update and return the UI to its previous state. This rollback is typically handled using the context returned from the onMutate callback. In the example above, you store the previous list of users before making the optimistic change. If the mutation fails, the onError callback uses this saved state to restore the UI, ensuring users see accurate information. This approach helps prevent confusion and maintains trust in your application's data integrity. Rollback strategies are essential whenever you use optimistic updates, as they protect against inconsistencies caused by failed mutations.

question mark

What should your application do if a mutation fails after performing an optimistic update?

Select the correct answer

Was alles duidelijk?

Hoe kunnen we het verbeteren?

Bedankt voor je feedback!

Sectie 3. Hoofdstuk 3

Vraag AI

expand

Vraag AI

ChatGPT

Vraag wat u wilt of probeer een van de voorgestelde vragen om onze chat te starten.

Suggested prompts:

Can you explain more about how the rollback works in this example?

What are some best practices for using optimistic updates in React?

Are there any risks or drawbacks to using optimistic updates?

bookOptimistic Updates

Veeg om het menu te tonen

Optimistic updates are a powerful technique that can make your React applications feel faster and more responsive. With optimistic updates, you update the UI immediately—before the server confirms that the mutation has succeeded. This approach gives users instant feedback for their actions, such as deleting an item or submitting a form, rather than waiting for a round-trip to the server.

The main benefit is a smoother user experience, especially for operations that might take a while to complete on the backend. Optimistic updates are especially useful in scenarios where you expect the mutation to succeed most of the time, and you want to minimize perceived latency in your application.

Suppose you have a list of users and want to let users delete entries from the list. With optimistic updates, you can remove the user from the UI right away, while the actual deletion request is still being processed by the server. Here is a simplified example of how you might implement this using TanStack Query's useMutation hook and the onMutate, onError, and onSettled callbacks:

import { useMutation, useQueryClient } from "@tanstack/react-query";

function UsersList({ users }) {
  const queryClient = useQueryClient();

  const deleteUserMutation = useMutation({
    mutationFn: (userId) =>
      fetch(`/api/users/${userId}`, { method: "DELETE" }),

    // Optimistically update the cache before the mutation happens
    onMutate: async (userId) => {
      await queryClient.cancelQueries({ queryKey: ["users"] });

      const previousUsers = queryClient.getQueryData(["users"]);

      queryClient.setQueryData(["users"], (old = []) =>
        old.filter((user) => user.id !== userId)
      );

      return { previousUsers };
    },

    // Roll back if the mutation fails
    onError: (err, userId, context) => {
      queryClient.setQueryData(["users"], context.previousUsers);
    },

    // Always refetch after error or success
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ["users"] });
    },
  });

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>
          {user.name}
          <button onClick={() => deleteUserMutation.mutate(user.id)}>
            Delete
          </button>
        </li>
      ))}
    </ul>
  );
}

In this example, when you click the Delete button, the user is immediately removed from the list in the UI. If the server deletion fails, the previous state is restored, ensuring data consistency.

Even with optimistic updates, there is always a chance that a mutation might fail—perhaps due to a network error or a server-side validation issue. In these situations, you need a strategy to roll back the optimistic update and return the UI to its previous state. This rollback is typically handled using the context returned from the onMutate callback. In the example above, you store the previous list of users before making the optimistic change. If the mutation fails, the onError callback uses this saved state to restore the UI, ensuring users see accurate information. This approach helps prevent confusion and maintains trust in your application's data integrity. Rollback strategies are essential whenever you use optimistic updates, as they protect against inconsistencies caused by failed mutations.

question mark

What should your application do if a mutation fails after performing an optimistic update?

Select the correct answer

Was alles duidelijk?

Hoe kunnen we het verbeteren?

Bedankt voor je feedback!

Sectie 3. Hoofdstuk 3
some-alt