← Все статьи

Офлайн в React: TanStack Query, IndexedDB и очередь исходящих

Три шага к устойчивым оптимистичным записям: кэш в IndexedDB, обработчики мутаций и очередь исходящих с идентификатором клиента на Dexie.

Офлайн в React: TanStack Query, IndexedDB и очередь исходящих
Содержание

Коротко

На Dev.to разобрали рабочий рецепт офлайн-записей в React: TanStack Query как реактивный кэш, Dexie поверх IndexedDB и небольшая очередь исходящих. Цель — чтобы добавление товара в «пятом ряду» пережило закрытие приложения и доехало до сервера уже в автобусе. Три опоры: сохранить кэш и приостановленные мутации, зарегистрировать обработчики по умолчанию, чтобы их можно было пересобрать после перезапуска, и выдавать клиентские идентификаторы плюс идемпотентный повтор.

Что произошло

Пользователь замечает не архитектуру, а поведение: запись не пропала после сбоя, слабой сети и убитого процесса. Автор предлагает считать TanStack Query зеркалом локальной базы, а не единственным местом правды. Сначала изменение пишут в Dexie, затем обновляют кэш запросов, а на сервер операция уходит, когда связь есть. Это ближе к блокноту в кармане, чем к форме, которая живёт только пока открыта вкладка: лист переживёт разряд батареи, а список покупок можно переписать в систему уже дома.

Первый шаг — сохранение реактивного кэша в IndexedDB через PersistQueryClientProvider и асинхронный persister. На старте приложение поднимает согласованный локальный снимок и вызывает resumePausedMutations. Важно выставить gcTime не короче срока жизни persister и через shouldDehydrateMutation сохранять только те приостановленные мутации, которые вы умеете возобновить. Иначе после перезапуска всплывут «зомби»: запись есть, а функции для повтора нет.

Второй шаг — queryClient.setMutationDefaults со стабильными ключами вроде ['items', 'create']. В сохранённой мутации лежат переменные и метаданные, но не замыкание с mutationFn. Обработчики по умолчанию нужно зарегистрировать до вызова возобновления, обычно импортом модуля при загрузке приложения. Третий шаг — клиентский идентификатор в onMutate, немедленная запись в таблицы Dexie и запись в очередь исходящих с действием, ключом мутации, переменными и временем. При появлении сети очередь дренируют по порядку, на сервер уходит заголовок вроде Idempotency-Key, успешные элементы из очереди удаляют.

Почему это важно

Обычный оптимистичный интерфейс без долговечного слоя врёт пользователю ровно до перезагрузки. Сеть «моргнула», вкладку закрыли — и локальная правка исчезла вместе с памятью процесса. Очередь исходящих и идентификатор клиента превращают повтор в безопасную операцию: сервер может получить один и тот же запрос дважды и не создать дубликат, если чтит ключ идемпотентности.

Платформа тоже диктует границы. Background Sync в Chrome помогает, в iOS Safari — нет. Поэтому повтор при появлении сети, при возврате вкладки и на старте приложения нужен как базовый контур, а не как опция. В нескольких вкладках без выбора ведущего процесса очередь обработают дважды; BroadcastChannel или Web Locks держат одного лидера. Экспоненциальный откат с дрожанием закрывает временные сбои, а постоянные ошибки проверки (обычно ответы 4xx) лучше снять с очереди и показать человеку, а не крутить бесконечно.

На практике

Статья даёт связку приёмов и наброски кода, не готовый пакет под ваш домен. Перед копированием решите, что является источником правды для записи — локальная база или кэш запросов.

  1. Пишите в Dexie первым; пусть TanStack Query реагирует на изменение локальных данных.
  2. Сохраняйте только возобновляемые приостановленные мутации; без обработчиков по умолчанию после перезапуска они бесполезны.
  3. Регистрируйте setMutationDefaults до resumePausedMutations.
  4. Ставьте клиентский идентификатор и кладите операцию в очередь исходящих в том же пользовательском жесте, что и оптимистичное обновление.
  5. На сервере чтите Idempotency-Key или номера последовательности, чтобы повтор не плодил дубликаты.
  6. Для связанных сущностей воспроизводите очередь по порядку; действие create должно прийти раньше update.
  7. При нескольких вкладках выберите одного лидера для опустошения очереди.

Итог

Устойчивый офлайн в React здесь складывается из трёх обещаний: состояние и очередь переживают смерть процесса в IndexedDB, приостановленные мутации можно пересобрать по обработчикам по умолчанию, повтор безопасен благодаря клиентским идентификаторам и дедупликации на сервере. Сеть остаётся ненадёжной — но пользователь продолжает работать, видит статус синхронизации и дожидается доставки, когда связь появляется.

Комментарии

Загрузка комментариев…