State Management

Как выбрать инструмент

Новый разработчик в команде заводит для списка товаров с сервера глобальный Redux-store, кладёт туда же выбранную вкладку из URL и флаг открытой модалки. Через месяц список разъезжается с сервером, кнопка назад в браузере не работает, а половина действий это ручная синхронизация кэша. Проблема не в Redux. Проблема в том, что четыре разных вида состояния свалены в один инструмент. Правильный выбор начинается с вопроса не как хранить, а что это за состояние.

  • Список товаров, профиль, цены с сервера - серверное состояние под TanStack Query
  • Активная вкладка, фильтры, строка поиска в URL - состояние URL под nuqs
  • Открытые панели, выбранный инструмент, черновик формы - клиентское состояние под Zustand
  • Мастер оформления заказа со сложными переходами шагов - конечный автомат под XState
  • Команды, разнёсшие свалку из одного store по подходящим инструментам и убравшие ручную синхронизацию

Предварительные знания

  • Различие серверного и клиентского состояния и почему его нельзя смешивать
  • Понимание, что данные с сервера это кэш, а не источник истины на клиенте
  • Идея кэша, свежести и инвалидации для серверных данных
  • Серверное и клиентское состояние

Дерево решений

Прежде чем выбирать библиотеку, состояние нужно классифицировать. Четыре вопроса по порядку отсекают неподходящие инструменты. Первый: приходит ли значение с сервера? Если да, это серверное состояние, и его место в кэше запросов, а не в клиентском store. Второй: должно ли значение жить в URL? Фильтры, вкладки и строка поиска просятся в адресную строку, чтобы работали ссылки и кнопка назад.

Третий вопрос: есть ли сложные переходы с правилами, какие состояния допустимы и из какого в какое можно перейти? Это область конечных автоматов вроде XState. Четвёртый: значение действительно глобально и нужно несвязанным частям приложения? Тогда общий клиентский store, например Zustand. Если ни один вопрос не дал да - значение локально, и достаточно обычного useState рядом с компонентом.

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

Список товаров приходит с сервера и показывается на нескольких страницах. Куда его положить по дереву решений?

Дефолтный стек 2026 года

Дерево решений на практике складывается в устоявшийся стек. К 2026 году дефолтный набор для типичного клиентского приложения такой: Zustand для клиентского состояния, TanStack Query для серверного, nuqs для состояния в URL. Каждый инструмент закрывает свой вид состояния и не лезет в чужой. Это не единственно возможный набор, а разумная отправная точка, требующая минимума обоснований.

Вид состоянияИнструментПримеры
СерверноеTanStack QueryСписок с API, профиль, цены, статус загрузки
Состояние URLnuqsФильтры, активная вкладка, строка поиска, страница пагинации
Клиентское глобальноеZustandОткрытые панели, выбранный инструмент, тема, черновик
Сложные переходыXStateМастер заказа, плеер, поток авторизации
ЛокальноеuseStateОткрыт ли один аккордеон, значение одного инпута

nuqs синхронизирует состояние с query-параметрами URL: смена сортировки меняет адрес, ссылку можно отправить коллеге, а кнопка назад возвращает прежний фильтр. Это снимает с клиентского store то, что по природе принадлежит URL. Связка трёх инструментов покрывает почти весь стейт типичного приложения без дублирования ответственности.

Серверные компоненты React сокращают потребность в глобальном клиентском store: данные, отрисованные на сервере, не нужно держать в клиентском состоянии. Это смещает дефолтный стек ещё дальше в сторону серверного состояния и URL, оставляя Zustand чисто клиентским мелочам.

Какой набор образует дефолтный стек состояния 2026 года для типичного клиентского приложения?

Распространённые антипаттерны

Большинство проблем со стейтом это не выбор плохой библиотеки, а помещение состояния не в тот вид инструмента. Самый частый антипаттерн - копия серверных данных в клиентском store, которую приходится вручную обновлять. Это превращает Zustand или Redux в самописный кэш без свежести и инвалидации, ровно ту боль, ради которой создан TanStack Query.

  • Серверные данные в клиентском store с ручной синхронизацией вместо кэша Query
  • Глобализация локального значения: один флаг открытого аккордеона переезжает в глобальный store без нужды
  • Фильтры и вкладки в клиентском store вместо URL, отчего ломаются ссылки и кнопка назад
  • Дублирование: одно и то же значение лежит и в Query, и в Zustand, и они расходятся
  • Конечный автомат, собранный из россыпи булевых флагов вместо явных состояний и переходов

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

Обратный антипаттерн - преждевременная глобализация. Значение, нужное одному компоненту, не стоит тащить в глобальный store на всякий случай. Это раздувает общий стейт и усложняет рассуждение о том, кто и когда его меняет. Правило: начинать с локального useState и поднимать состояние выше только когда появился реальный второй потребитель.

Лечение свалки в одном store

Каждый вид состояния попал в подходящий инструмент, и ручная синхронизация исчезла

Был один Redux-store с массивом товаров, активной вкладкой и флагом модалки. Массив переехал в TanStack Query (исчезла ручная синхронизация и появилось фоновое обновление), вкладка в nuqs (заработали ссылки и кнопка назад), флаг модалки остался локальным useState. Глобальным остался лишь выбранный инструмент редактора в Zustand.

В клиентском store есть поле products, которое после каждого ответа API вручную перезаписывается. Что это за сигнал?

Связь с другими темами

Этот урок про выбор. Конкретные инструменты разобраны отдельно:

  • Серверное и клиентское состояние — Первая развилка дерева решений: данные с сервера это кэш, а не клиентское состояние
  • Zustand — Клиентская часть дефолтного стека: точечные ре-рендеры через селекторы без Provider
  • TanStack Query — Серверная часть дефолтного стека: кэш, свежесть и инвалидация серверных данных

Итог

  • Выбор инструмента начинается с вопроса не как хранить, а каков вид состояния
  • Дерево решений: данные с сервера, состояние URL, сложные переходы, действительно глобальное клиентское значение
  • Серверное состояние держит TanStack Query, не клиентский store: это кэш с свежестью и инвалидацией
  • Состояние URL (фильтры, вкладки, поиск) кладут в адресную строку через nuqs ради ссылок и кнопки назад
  • Сложные переходы с правилами и невозможными состояниями описывает конечный автомат, например XState
  • Дефолтный стек 2026 года: Zustand для клиентского, TanStack Query для серверного, nuqs для URL

Связанные уроки

  • sm-30-server-vs-client-state — Разделение серверного и клиентского состояния это первая развилка дерева решений и фундамент всего выбора
  • rc-38-zustand-state — Zustand это клиентская часть дефолтного стека 2026 года, разобранная на реальном коде React
  • rc-36-tanstack-query — TanStack Query закрывает серверное состояние в дефолтном стеке, его кэш и свежесть разобраны отдельно
Как выбрать инструмент

0

1

Войти