Optimistic updates show changes immediately before server confirms.
Code:
1import { useState, useTransition } from "react";23function TodoList() {4 const [todos, setTodos] = useState<Todo[]>([]);5 const [isPending, startTransition] = useTransition();67 const addTodo = async (text: string) => {8 const tempId = Date.now();910 // Optimistic update11 startTransition(() => {12 setTodos(prev => [...prev, { id: tempId, text, done: false }]);13 });1415 try {16 const newTodo = await api.addTodo(text);17 // Replace temp with real18 setTodos(prev => prev.map(t =>19 t.id === tempId ? newTodo : t20 ));21 } catch (error) {22 // Revert on error23 setTodos(prev => prev.filter(t => t.id !== tempId));24 }25 };2627 return (28 <div>29 {todos.map(todo => (30 <div key={todo.id}>{todo.text}</div>31 ))}32 <button onClick={() => addTodo("New Todo")}>Add</button>33 </div>34 );35}
With React 19 useOptimistic:
1import { useOptimistic } from "react";23function TodoList({ todos, addTodo }) {4 const [optimisticTodos, addOptimistic] = useOptimistic(5 todos,6 (state, newTodo: string) => [7 ...state,8 { id: Date.now(), text: newTodo, done: false, pending: true },9 ]10 );1112 const handleSubmit = async (formData) => {13 const title = formData.get("title");14 addOptimistic(title);15 await addTodo(title);16 };1718 return (19 <div>20 {optimisticTodos.map(todo => (21 <div key={todo.id} style={{ opacity: todo.pending ? 0.5 : 1 }}>22 {todo.text}23 </div>24 ))}25 </div>26 );27}
Benefits:
Pitfalls: