React Query supports optimistic updates with rollback.
Code:
1import { useMutation, useQueryClient } from "@tanstack/react-query";23function TodoList({ todos }) {4 const queryClient = useQueryClient();56 const addTodoMutation = useMutation({7 mutationFn: (newTodo) => api.addTodo(newTodo),8 onMutate: async (newTodo) => {9 // Cancel outgoing refetches10 await queryClient.cancelQueries({ queryKey: ["todos"] });1112 // Snapshot previous value13 const previousTodos = queryClient.getQueryData(["todos"]);1415 // Optimistically update16 queryClient.setQueryData(["todos"], (old) => [17 ...old,18 { id: Date.now(), text: newTodo.text, done: false },19 ]);2021 return { previousTodos };22 },23 onError: (err, newTodo, context) => {24 // Rollback on error25 queryClient.setQueryData(["todos"], context.previousTodos);26 },27 onSettled: () => {28 // Refetch to sync with server29 queryClient.invalidateQueries({ queryKey: ["todos"] });30 },31 });3233 return (34 <div>35 {todos.map(todo => (36 <div key={todo.id}>{todo.text}</div>37 ))}38 <button onClick={() => addTodoMutation.mutate({ text: "New Todo" })}>39 Add40 </button>41 </div>42 );43}
Benefits: