В TanStack Query мутации представляют собой операции изменения
серверного состояния: создание, обновление или удаление данных. В
отличие от запросов, мутации не просто кешируются, а проходят через
жизненный цикл статусов: idle, pending,
success, error.
Персистентные мутации — это механизм сохранения состояния незавершённых или частично завершённых мутаций между сессиями приложения. Это включает восстановление очереди мутаций после перезагрузки страницы, восстановления соединения или повторного открытия приложения.
Ключевая цель — обеспечить непрерывность операций записи в условиях нестабильного окружения (offline-first, mobile web, слабое соединение).
Сохранение мутаций становится критически важным в сценариях:
В таких условиях отсутствие персистенции приводит к потере пользовательских действий и рассинхронизации клиентского состояния с сервером.
В TanStack Query мутации хранятся в MutationCache.
Каждая мутация описывается объектом, содержащим:
mutationKeystate (status, variables, data, error)metamutationFn)context)Внутренне MutationCache управляет:
Для персистенции важно, что именно MutationCache
является источником истины для всех активных мутаций.
Персистентность невозможна без сериализации. Однако мутации сложнее запросов, так как содержат:
mutationFn)Поэтому сохраняется только безопасная часть состояния:
mutationKeystate.statusstate.datastate.error (частично сериализуемый)variablessubmittedAtmetaФункции выполнения при восстановлении не сохраняются и должны быть восстановлены через конфигурацию клиента.
Персистентность реализуется через persistQueryClient из
@tanstack/query-persist-client-core.
Базовая схема:
import { QueryClient } from '@tanstack/react-query'
import { persistQueryClient } from '@tanstack/query-persist-client-core'
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister'
const queryClient = new QueryClient({
defaultOptions: {
mutations: {
retry: 3
}
}
})
const persister = createSyncStoragePersister({
storage: window.localStorage
})
persistQueryClient({
queryClient,
persister
})
По умолчанию сохраняются query cache и mutation cache, если они не исключены настройками.
После восстановления приложения происходит гидратация состояния:
MutationCachepending,
paused, error)Ключевой момент: TanStack Query не выполняет автоматически повтор всех мутаций. Разработчик должен явно определить стратегию:
Пример ручной обработки:
queryClient.resumePausedMutations()
При отсутствии сети или ошибке выполнения мутации могут переходить в
состояние paused.
Это означает:
resumePausedMutationsТипичный сценарий:
pausedПри восстановлении мутаций возникает проблема дубликатов:
Для решения используются:
Позволяет группировать одинаковые операции:
useMutation({
mutationKey: ['todo', 'update'],
mutationFn: updateTodo
})
На уровне API:
Персистентные мутации наиболее полезны в offline-first архитектуре.
Типичный поток:
pausedДополнительно используется:
onlineManagerwindow.addEventListener('online')resumePausedMutationsПосле восстановления соединения важно управлять повторными попытками:
const queryClient = new QueryClient({
defaultOptions: {
mutations: {
retry: (failureCount, error) => {
if (error?.status === 401) return false
return failureCount < 3
}
}
}
})
При персистентных мутациях retry становится частью стратегии синхронизации, а не просто обработкой ошибки.
После успешного восстановления и выполнения мутации часто требуется обновить query cache:
queryClient.invalidateQueries({ queryKey: ['todos'] })
В offline-first сценариях инвалидация может быть отложенной до завершения очереди мутаций.
Несмотря на гибкость, механизм имеет ограничения:
После гидратации важно учитывать, что каждая мутация может находиться в одном из состояний:
idle — не выполняласьpending — выполняетсяpaused — ожидает сетиerror — завершилась ошибкойsuccess — выполненаКонтроль осуществляется через:
queryClient.getMutationCache().getAll()
и дальнейшую фильтрацию по состоянию.
Для стабильной работы обычно вводится слой оркестрации:
Часто используется комбинация:
MutationCachepersistQueryClientonlineManagerТипичный порядок инициализации:
QueryClientawait persistQueryClient({
queryClient,
persister
})
queryClient.resumePausedMutations()
В реальных приложениях персистентные мутации применяются для:
В таких системах мутации становятся не просто HTTP-запросами, а элементами локальной очереди событий.
Для предотвращения рассинхронизации используется:
Особое внимание уделяется тому, чтобы восстановленные мутации не перезаписывали более свежие серверные данные.
Если несколько мутаций изменяют один ресурс:
TanStack Query не навязывает стратегию, оставляя её на уровне архитектуры приложения.