TanStack Vue Query: мутации и optimistic updates

↑ 5.2 Состояние: Pinia, SSOT, Vue Query · ← Предыдущая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Как отправлять изменения и мгновенно обновлять UI с корректным откатом.

Объяснение

Мутация — операция изменения на сервере (POST/PUT/DELETE); в отличие от запроса, не кэшируется и запускается вручную.

const qc = useQueryClient();
const { mutate, mutateAsync, isPending, error } = useMutation({
  mutationFn: (dto: UpdateOrder) => api.updateOrder(dto.id, dto),
  onSuccess: (updated) => {
    qc.setQueryData(["order", updated.id], updated);                 // точечно
    qc.invalidateQueries({ queryKey: ["orders"] });                  // список — перезапрос
    notify("Сохранено");
  },
  onError: (e) => notify(errorMessage(e), "negative"),
  onSettled: () => qc.invalidateQueries({ queryKey: ["orders"] }),
});
mutate(dto, { onSuccess: () => router.back() });

Оптимистичное обновление: UI меняется сразу, откат при ошибке.

useMutation({
  mutationFn: (id: number) => api.toggleFavorite(id),
  onMutate: async (id) => {
    await qc.cancelQueries({ queryKey: ["orders"] });                // отменить исходящие запросы, чтобы не затёрли
    const previous = qc.getQueryData<Order[]>(["orders"]);           // снимок для отката
    qc.setQueryData<Order[]>(["orders"], (old) => old?.map(o => o.id === id ? { ...o, favorite: !o.favorite } : o));
    return { previous };
  },
  onError: (_e, _id, ctx) => qc.setQueryData(["orders"], ctx?.previous),    // откат
  onSettled: () => qc.invalidateQueries({ queryKey: ["orders"] }),          // синхронизация с сервером
});

В новых версиях можно оптимистично показывать variables мутации прямо в UI (mutation.variables) без правки кэша.

Практики:

  • Идемпотентность и Idempotency-Key для повторов.
  • Блокировка кнопки на время isPending, защита от двойного клика.
  • Последовательные мутации: scope (очередь), mutateAsync + await.
  • Ошибки валидации сервера (422) → поля формы.
  • Офлайн: networkMode, persistQueryClient.

Нюансы и подводные камни

  • Без cancelQueries фоновый запрос может перезаписать оптимистичное значение.
  • Откат должен восстанавливать именно снимок до изменений.
  • Инвалидация в onSettled гарантирует итоговую согласованность.
  • Оптимизм оправдан для частых и предсказуемых операций (лайк, чекбокс); для платежей — нет.
  • Ключи кэша: обновляйте все затронутые (список, карточка).

Практика

  1. Реализуйте оптимистичное переключение статуса с откатом при ошибке.
  2. После создания заказа инвалидируйте список и перейдите на карточку.
  3. Защитите форму от повторной отправки.

Вопросы с ответами

Связанные темы