State Management

Zustand

В редакторе досок нужно расшарить три значения между панелью инструментов, холстом и инспектором: выбранный инструмент, уровень зума и id выделенного объекта. Поднять их в общий компонент означает прокидывать пропсы через половину дерева. Завести Redux ради трёх полей означает actions, reducers, store и провайдер. Zustand предлагает третий путь: стор это функция create, возвращающая хук, без провайдера вокруг приложения, а компонент подписывается селектором ровно на тот кусок, который ему нужен. Около 1.2 КБ в бандле, и к 2026 году это самый популярный клиентский стор.

  • UI-состояние редакторов и канвасов: выбранный инструмент, зум, выделение в одном лёгком сторе
  • Глобальные UI-флаги: открытые модалки, активная тема, состояние сайдбара без провайдера
  • Мастера и многошаговые формы, где состояние шарится между несвязанными шагами
  • Сохранение части состояния между сессиями через middleware persist в localStorage
  • Команды, заменившие Redux на Zustand ради меньшего объёма кода при той же предсказуемости

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

  • Единый источник истины: одно общее состояние вместо копий по компонентам
  • Хуки и подписка компонента на изменение значения
  • Сравнение по ссылке и причина лишних ре-рендеров при новом объекте
  • Единый источник истины

Откуда взялся минимальный стор

Zustand (по-немецки состояние) появился в 2019 году в коллективе Poimandres, известном по react-three-fiber. Авторам нужен был стор для 3D-сцен, где Redux ощущался тяжёлым, а Context вызывал лишние ре-рендеры. Идея вышла радикально простой: стор это хук, созданный функцией create, без провайдеров и без обязательного редьюсера. Компонент сам выбирает селектором нужный кусок и ре-рендерится только при его изменении. К середине 2020-х Zustand стал дефолтным выбором для клиентского состояния, а версия 5 закрепила маленький размер около 1.2 КБ и набор middleware. По числу еженедельных загрузок (десятки миллионов) это самый используемый клиентский стор в 2026 году.

Стор через create без Provider

В Zustand стор создаётся функцией create. Она принимает функцию инициализации, которой передан set, и возвращает хук. Внутри объявляются и состояние, и действия, меняющие его через set. Никакого провайдера оборачивать не нужно: хук обращается к стору напрямую из любого компонента, потому что стор это модуль-синглтон.

Действия increment и reset это обычные функции внутри стора, меняющие состояние через set. set делает поверхностное слияние: достаточно вернуть только изменившиеся поля, остальные сохранятся. Никаких отдельных типов action, редьюсеров и dispatch нет - стор это данные плюс методы в одном месте.

Отсутствие Provider это не косметика. Стор это синглтон, к которому есть доступ и из React через хук, и из обычного кода через getState и subscribe. Это удобно для интеграции с не-React частями: вебсокетами, таймерами, аналитикой, которые читают и меняют состояние напрямую.

Что принципиально отличает создание стора в Zustand от настройки Provider-based решений?

Подписка селектором против лишних ре-рендеров

Хук стора вызывается с селектором - функцией, которая выбирает из стора нужный кусок. Компонент перерисовывается только тогда, когда именно этот кусок изменился, а не при любом изменении стора. Это и есть подписка: чем уже селектор, тем реже компонент ре-рендерится. Выбор одного примитива сравнивается по значению и не требует ничего дополнительного.

Селектор, возвращающий новый объект на каждом вызове, ломает оптимизацию. Выражение вида { a: s.a, b: s.b } создаёт новую ссылку каждый раз, и компонент ре-рендерится без причины. Для выбора нескольких полей берут useShallow, чтобы сравнение шло поверхностно по значениям, а не по ссылке.

Так стор может быть большим и общим, но каждый компонент платит ре-рендером только за свою часть. Выбор одного значения сравнивается по значению напрямую, выбор нескольких полей оборачивают в useShallow. Этот приём заменяет ту работу, которую в Redux выполняют мемоизированные селекторы reselect.

Зачем при чтении из стора Zustand передавать узкий селектор?

Middleware: persist, immer, devtools

Базовый стор обходится без обвязки, но при необходимости его оборачивают в middleware. Это функции, которые принимают инициализатор стора и возвращают расширенный. Три самых частых: persist сохраняет состояние в хранилище и восстанавливает при загрузке, immer добавляет мутирующий синтаксис в set, devtools подключает стор к Redux DevTools для просмотра действий.

Здесь persist оборачивает инициализатор и под ключом app-settings сохраняет состояние в localStorage по умолчанию. После перезагрузки страницы тема восстановится из хранилища. Двойной вызов create()(...) это способ дать TypeScript корректно вывести типы при оборачивании в middleware: первая пара скобок фиксирует тип состояния, вторая принимает обёрнутый инициализатор.

MiddlewareЧто добавляетКогда нужно
persistСохранение и восстановление из хранилищаТема, онбординг, черновики между сессиями
immerМутирующий синтаксис внутри setГлубокие вложенные обновления без спредов
devtoolsПросмотр действий в Redux DevToolsОтладка и трассировка изменений стора

Middleware можно комбинировать, оборачивая один в другой, например devtools(persist(immer(...))). Порядок имеет значение: devtools обычно ставят снаружи, чтобы он видел действия после остальных обёрток. Базовый стор при этом остаётся прежним, middleware лишь надстраивают над ним возможности.

Какое middleware Zustand сохраняет состояние стора между перезагрузками страницы?

Почему Zustand стал самым популярным

Популярность Zustand складывается из нескольких свойств. Размер около 1.2 КБ почти не влияет на бандл. Нет провайдера и нет обязательного редьюсера, поэтому порог входа низкий. Подписка селектором даёт точечные ре-рендеры без отдельной библиотеки мемоизации. Стор-синглтон доступен и вне React. По числу еженедельных загрузок (десятки миллионов) это самый используемый клиентский стор в 2026 году.

  • Zustand для клиентского состояния — Минимум кода, без провайдера, точечные ре-рендеры селектором, около 1.2 КБ. Подходит для большинства общего клиентского состояния
  • Redux для крупного потока — Строгий поток, devtools с перемоткой, нормализация, middleware. Окупается в больших приложениях со сложным переплетённым потоком

Zustand про клиентское состояние, а не про серверное. Если поле в сторе это копия ответа сервера, которую приходится вручную обновлять, это сигнал перенести его в query-слой. Превращать стор в самописный кэш серверных данных не стоит: свежесть, кэш и инвалидация это задача RTK Query или TanStack Query.

Практический ориентир на 2026 год: для большинства общего клиентского состояния Zustand даёт точечные ре-рендеры при минимуме кода. Redux выбирают осознанно под крупный сложный поток. Серверные данные в любом случае держат в query-библиотеке. Так каждый слой состояния закрывается подходящим инструментом, а не одним на всё.

Какое состояние НЕ стоит держать в Zustand?

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

Zustand это лёгкий стор-синглтон, соседствующий с Redux и взглядом из React:

  • Единый источник истины — Zustand реализует идею одного источника истины как модуль-синглтон, доступный из любого компонента
  • Zustand в курсе React — Параллельный разбор того же стора с упором на React и сравнение с поведением Context
  • Redux: паттерны и когда оправдан — Граница применимости: где обвязка Redux избыточна, Zustand даёт то же без adapter и middleware-обвязки

Итог

  • Zustand создаёт стор функцией create: стор это хук, без Provider вокруг приложения и без обязательного редьюсера
  • Действия живут прямо в сторе как обычные функции и меняют состояние через set с поверхностным слиянием
  • Компонент подписывается селектором на конкретный кусок и ре-рендерится только при изменении именно его
  • Стор это модуль-синглтон: доступ есть и из React через хук, и из обычного кода через getState/subscribe
  • Middleware persist сохраняет состояние, immer даёт мутирующий синтаксис, devtools подключает Redux DevTools
  • Около 1.2 КБ и десятки миллионов еженедельных загрузок делают Zustand самым популярным клиентским стором 2026 года

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

  • sm-03-single-source-of-truth — Zustand это один источник истины в виде стора-синглтона: идея единого состояния задаёт контекст для create
  • rc-38-zustand-state — Тот же Zustand с акцентом на React и сравнение с Context: параллельный взгляд из курса React
  • sm-16-redux-patterns — Там, где обвязка Redux избыточна, Zustand закрывает то же клиентское состояние меньшим кодом
Zustand

0

1

Войти