Оптимістичне оновлення - показати результат дії одразу, не чекаючи відповіді сервера, і відкотити, якщо сервер повернув помилку. Інтерфейс відчувається миттєвим: «лайк», позначка завдання, перейменування.
Варіант через кеш запиту:
const queryClient = useQueryClient();
const toggleTodo = useMutation({
mutationFn: (todo) =>
fetch(`/api/todos/${todo.id}`, { method: 'PATCH', body: JSON.stringify({ done: !todo.done }) }),
onMutate: async (todo) => {
await queryClient.cancelQueries({ queryKey: ['todos'] }); // 1
const previous = queryClient.getQueryData(['todos']); // 2
queryClient.setQueryData(['todos'], (old) => // 3
old.map((t) => (t.id === todo.id ? { ...t, done: !t.done } : t)),
);
return { previous };
},
onError: (error, todo, context) => {
queryClient.setQueryData(['todos'], context.previous); // 4
toast.error('Не вдалося зберегти');
},
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), // 5
});
Кроки й навіщо кожен:
- скасувати поточні запити списку - інакше відповідь, що вже летить, перезапише оптимістичні дані старими;
- зберегти знімок для відкату;
- оновити кеш - інтерфейс змінюється миттєво;
- при помилці - відкотити до знімка і повідомити користувача;
- після завершення - перезапитати дані з сервера, щоб кеш точно відповідав реальності.
Простіший варіант - через змінні мутації: не чіпати кеш, а в компоненті показувати mutation.variables, поки мутація в стані isPending. Менше коду, відкат автоматичний (просто зникає тимчасове відображення), але зміна видна лише там, де рендериться мутація.
React 19 useOptimistic - схожа ідея для дій і форм без бібліотеки: тимчасовий стан, що діє до завершення асинхронної дії.
Коли оптимізм доречний: дія майже завжди успішна, легко відкочується і не має незворотних наслідків.
Коли ні: оплати, відправка листів, видалення без можливості відновлення, операції з високим шансом відмови через валідацію - там краще чесний стан «Зберігаємо...».
Пастки:
- кілька швидких мутацій однієї сутності: відкат однієї може затерти іншу - враховувати це при розробці
onError(чи серіалізувати мутації); - згенеровані сервером поля (id, дати) в оптимістичному записі - тимчасові значення, які замінить відповідь;
- повідомлення про відкат обов'язкове - тихе «повернення» стану користувач сприйме як баг.
Докладніше в документації: TanStack Query: оптимістичні оновлення