Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lære 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

Alt var klart?

Hvordan kan vi forbedre det?

Takk for tilbakemeldingene dine!

Seksjon 3. Kapittel 3

Spør AI

expand

Spør AI

ChatGPT

Spør om hva du vil, eller prøv ett av de foreslåtte spørsmålene for å starte chatten vår

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

Sveip for å vise menyen

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

Alt var klart?

Hvordan kan vi forbedre det?

Takk for tilbakemeldingene dine!

Seksjon 3. Kapittel 3
some-alt