Redux Toolkit против Zustand и Jotai в сложных React-приложениях
Redux Toolkit против Zustand и Jotai: практичный способ выбрать подход для сложного React-состояния, исходя из рабочих процессов команды, потребностей в отладке и меняющихся бизнес-правил.

Почему выбор быстро становится сложным
Состояние кажется простым, когда в приложении есть одна форма, один пользователь и один удачный сценарий. Потом приложение растёт. Клиент редактирует профиль на одном экране, служба поддержки открывает тот же аккаунт на другом, а фоновое обновление подгружает новые данные в самый неподходящий момент.
Именно тогда начинается сложное React-состояние. Общие данные переходят между экранами. Асинхронные запросы завершаются не по порядку. Производные значения должны оставаться корректными. Бизнес-правила постоянно меняются, а интерфейс при этом всё равно должен ощущаться быстрым.
Простые демо скрывают большую часть этих проблем. Счётчик или список задач не показывают, что происходит, когда одно изменение одновременно влияет на таблицу, страницу деталей, бейдж уведомлений и проверку прав доступа. Реальное приложение может пересчитывать итог заказа, блокировать действие для части пользователей и сохранять черновик после повторной загрузки данных.
Сложность выбора между Redux Toolkit, Zustand и Jotai не в API. Она в том, как каждый инструмент ведёт себя под настоящей нагрузкой. Одно и то же состояние начинает затрагивать формы, списки, боковые панели, фоновую синхронизацию и запросы к серверу. Кому-то в команде нужно будет объяснить, почему состояние изменилось, в каком порядке и после какого действия пользователя. А потом правила снова изменятся.
Общие данные расползаются незаметно. Фильтр на одном экране меняет график в другом месте. Обновление роли меняет, какие кнопки видны сразу в трёх местах. То, что в демо выглядело аккуратно, через несколько месяцев может превратиться в путаницу, особенно если два разработчика решают одну и ту же задачу по-разному.
Именно поэтому команды застревают на этом выборе. Они выбирают не маленькую утилиту. Они выбирают, как приложение будет справляться с растущими правилами, багами и повседневными изменениями.
Сначала смотрите на работу, а не на библиотеку
Команды часто начинают со списка возможностей и звёзд на GitHub. Это ошибка. Лучше сначала посмотреть на саму работу, которую уже делает ваше приложение.
Запишите, где сегодня живёт состояние. Проверьте component state, context, form libraries, URL params, cached server responses и любые custom store, которые уже есть. Многие приложения выглядят хаотично не потому, что была выбрана не та библиотека, а потому, что никто не может сказать, какое состояние где должно находиться.
Потом посчитайте, сколько экранов обращаются к одним и тем же данным. Если запись пользователя появляется в таблице, на странице деталей, в модальном окне редактирования, в журнале действий и панели прав доступа, у вас уже есть давление общего состояния. Когда одни и те же данные проходят через несколько экранов, слабая схема начинает трещать.
Разделяйте server data и UI state. Server data — это заказы, пользователи, возвраты и всё, что вы получаете и синхронизируете. UI state — это открытые вкладки, фильтры, выбранные строки, черновики текста и состояние модального окна. Команды постоянно смешивают эти две вещи, а потом обвиняют библиотеку, когда обновления начинают казаться запутанными.
Быстрый аудит обычно говорит больше, чем любая сравнительная таблица:
- Что приходит с backend
- Что важно только на одном экране
- Что несколько экранов редактируют или от чего зависят
- Какие правила разработчики скопировали в несколько мест
Последний пункт особенно важен. Если одно правило для refunds живёт на странице заказа, другое — в админ-панели, а третье — в проверке кнопки, проблема не только в состоянии. Это дублирование бизнес-логики. Библиотека состояния может помочь, но только после того, как дублирование станет видно.
На самом деле это не спор о стиле. Это вопрос о том, сколько у вас общего состояния, как часто меняются правила и сколько ручной координации команда делает сегодня. Честно оцените это — и ответ станет яснее.
Redux Toolkit, когда правила накапливаются
Redux Toolkit лучше всего подходит там, где состояние несёт бизнес-правила, а не просто UI-флаги. Если одно действие пользователя может запустить проверки прав, смену статуса, запись в журнал и последующие запросы, лучше держать эту логику в одном понятном месте.
Reducers работают потому, что заставляют команду написать правило один раз. Вместо того чтобы размазывать "может ли пользователь одобрить действие и находится ли заказ ещё в ожидании" по компонентам, hooks и helper-ам, вы оставляете это решение внутри slice-логики и selectors. Когда правило меняется, обычно сначала меняется один файл, а не шесть.
Action names помогают сильнее, чем кажется. Названия вроде "refundApproved", "roleChanged" или "invoiceLocked" дают всей команде один и тот же язык в code review, в баг-репортах и на планировании. Это звучит мелко, но сильно снижает путаницу, когда продукт, поддержка и инженеры обсуждают один и тот же сценарий.
Redux Toolkit также обычно лучше живёт со временем, когда один и тот же процесс трогают многие люди. Общие паттерны упрощают проверку изменений, а отладка становится менее хаотичной, потому что действия и обновления состояния оставляют понятный след. В сложном приложении этот след важнее, чем небольшая разница в скорости настройки.
Есть и реальная цена. Для небольших функций slices, selectors и дополнительная связка могут ощущаться как лишняя бумажная работа. Если модальное окно только хранит состояние открытия и два поля формы, Redux Toolkit, скорее всего, слишком тяжёлый.
Выбирайте его, когда правила часто меняются и команда всё время добавляет исключения. Обычно это лучший вариант тогда, когда сложно не хранить данные, а удерживать бизнес-логику последовательной после пятой переработки.
Zustand, когда скорость важнее структуры
Zustand часто выигрывает первый месяц. Вы быстро создаёте store, добавляете несколько actions и запускаете экран без лишней церемонии. Это важно, когда маленькая команда ещё проверяет идеи и меняет продукт каждые несколько дней.
Привлекательность в том, насколько он прямолинеен. Вы читаете состояние, вызываете функцию обновления и идёте дальше. Для filters, modal state, selected items, draft inputs и небольших feature flags такой простой цикл помогает людям работать быстрее.
Хороший пример — админ-панель стартапа. Если команда каждую неделю тестирует разные варианты заказов, меняет подписи для refunds и перерабатывает approval flow, небольшой Zustand store делает работу легче. Никому не нужно заранее строить сложную структуру, прежде чем станет понятно, что на самом деле нужно продукту.
Проблемы начинаются, когда один store становится местом для всего. Orders, refunds, roles, audit notes, loading states и async actions оказываются в одном файле. Код по-прежнему выглядит коротким, но бизнес-правила становятся размытыми. Разработчик меняет одно поле, а три экрана реагируют неожиданным образом.
Zustand лучше всего подходит, когда состояние остаётся близко к одной части приложения и правила легко объяснить. Он хорошо работает для page filters, sort state, коротких шагов мастера, локального UI state вокруг одной функции и admin tools, которые в основном читают server data.
Он становится слабее, когда команде нужен понятный ответ на вопросы: почему состояние изменилось, кто его изменил и какое правило это позволило. Если refunds зависят от роли аккаунта, возраста заказа, fraud checks и этапа ручной проверки, слишком свободная логика store быстро превращается в догадки.
Выбирайте Zustand, когда скорость важнее структуры и когда один разработчик может объяснить поток за минуту. Если это перестаёт быть правдой, store, скорее всего, уже вышел за пределы своего удобного сценария.
Jotai, когда состояние делится на маленькие части
Jotai часто лучше всего подходит там, где экран похож на панель управления. Думайте о filters, tabs, drawers, inline forms, selected rows, modal state и нескольких вычисляемых значениях, которые зависят друг от друга.
Он хорошо работает, потому что каждый atom может хранить одну небольшую часть состояния. Поле поиска может использовать один atom, активный элемент возврата — другой, а проверка прав — derived atom. Это помогает разделять несвязанные части, так что изменение одного поля заметки не затягивает половину страницы в обновление.
Страница админки хорошо показывает его сильные стороны. Orders, причины возвратов, роли пользователей, открытые панели и черновики комментариев не обязаны жить в одном большом объекте. С Jotai каждая часть может оставаться рядом с той функцией, которая её использует. Команды часто двигаются быстрее, когда могут добавить один новый atom вместо того, чтобы каждый раз перестраивать центральный store ради мелкого UI-правила.
Где Jotai начинает мешать
Та же свобода может стать проблемой. Если правила для refunds живут в одном atom, проверки ролей — в другом файле, а totals — в трёх derived atoms, логика расползается. Через несколько месяцев исправление одного бага может превратиться в отслеживание цепочки маленьких частей по всему приложению.
В этом и состоит компромисс. Jotai даёт тонкий контроль, но при этом слишком легко раскидать бизнес-правила по разным местам. Когда правила меняются на многих экранах, это быстро утомляет.
Jotai обычно хорош, когда на экранах много независимых частей UI, функции требуют точного локального контроля, команда вносит изменения маленькими шагами, а большинство правил остаётся внутри одной области. Если в приложении много движущихся частей, но мало политики, которая затрагивает всё приложение, Jotai выглядит чисто и прямо. Если же правила начинают касаться всего, аккуратные маленькие atoms перестают казаться маленькими.
Отладка меняет ответ
Библиотека состояния может радовать месяцами, а потом один неприятный баг меняет мнение. Возврат не проходит после обновления роли, повторного открытия модального окна и ещё одного клика. В этот момент чистый синтаксис уже менее важен, чем возможность точно увидеть, что произошло.
Redux Toolkit обычно выигрывает, когда команде нужна понятная история событий. Actions идут по порядку, DevTools показывают след, а повторить баг становится реально. Если support говорит: "Пользователь одобрил заказ, сменил права, а потом снова попробовал сделать возврат", разработчик может посмотреть на эту цепочку, а не гадать по скриншотам.
Zustand быстрее на старте и часто проще читается сначала. Но у него есть проблема с прослеживаемостью. Можно добавить devtools, логи и соглашения, но многие команды пишут обновления слишком прямо, и позже их сложнее распутывать. У Jotai похожая слабость, но в другой форме. Небольшие atoms держат код аккуратным, но когда один баг проходит через несколько atoms, путь не всегда очевиден.
Служба поддержки обычно чувствует это раньше разработчиков. Им не нужна элегантная модель состояния. Им нужны достаточные детали, чтобы воспроизвести проблему: что изменилось, в каком порядке и какой экран всё запустил. Понятные журналы событий могут сэкономить часы, особенно когда одна и та же проблема появляется раз в неделю и только у части ролей.
Задайте несколько прямых вопросов:
- Может ли команда видеть каждое изменение состояния по порядку?
- Может ли кто-то повторить баг без того, чтобы просить у пользователя видео?
- Влияют ли permissions, billing или workflow steps друг на друга?
- Как часто две функции ломают друг друга после релиза?
Если ответ — "часто", обычно безопаснее выбрать Redux Toolkit. Если баги остаются локальными, а команда может понять их из component code, Zustand или Jotai тоже подойдут. Отладка — это не второстепенный вопрос. Она влияет на стоимость каждого будущего бага.
Как выбрать за пять шагов
Игрушечный счётчик почти ничего не говорит о реальных проблемах состояния. Возьмите одну настоящую функцию, с которой команда уже борется, например заказы, которые влияют на refunds, permissions и audit logs. Реальная работа показывает, где инструмент выглядит понятным, а где начинает прятать логику.
- Выберите одну сложную производственную функцию. Возьмите что-то с async requests, derived values, loading states и поведением, зависящим от роли. Если эта функция уже вызывает долгие обсуждения в Slack, это хороший тест.
- Запишите три недавних бага простыми словами. "Refund total остался старым после смены роли" говорит намного больше, чем "проблема со state".
- Оцените, как часто меняются бизнес-правила. Если продукт постоянно меняет шаги approval, лимиты редактирования или permissions по ролям, структура становится важной.
- Оцените, насколько важна возможность повторить баг. Если QA, support или finance часто должны отвечать на вопрос "как это произошло?", вам нужна понятная история обновлений.
- Выберите самый простой инструмент, который всё ещё объясняет поток. Если новый член команды может отследить изменения состояния за десять минут, этого инструмента, скорее всего, достаточно.
Этот тест лучше любого списка возможностей. Zustand часто выигрывает, когда функция насыщенная, но всё ещё легко читается. Jotai хорошо подходит, когда состояние распадается на много маленьких связанных частей. Redux Toolkit заслуживает лишней церемонии тогда, когда правила постоянно меняются и команде нужно воспроизводить баги, а не спорить о них.
Если два варианта всё ещё кажутся одинаковыми, выбирайте тот, с которым самые неприятные баги будет проще отлаживать.
Пример: админ-приложение с orders, refunds и roles
Представьте админ-приложение для компании среднего размера. Сотрудники поддержки проверяют заказы, finance обрабатывает refunds, а менеджеры видят totals по команде, региону и диапазону дат. Все используют одни и те же filters, и totals должны совпадать на каждом экране.
Именно здесь простые решения начинают ломаться. Если один экран обновляет refund, другому может понадобиться пересчитать totals, обновить проверки прав и скрыть действия, которые обычный сотрудник видеть не должен.
Теперь добавьте сложность: правила для refunds меняются каждый месяц. В один месяц partial refunds требуют одобрения менеджера выше определённой суммы. В следующий месяц цифровые товары работают по другому правилу. Потом появляются исключения для VIP-клиентов. Для бизнес-софта это нормально.
Для такого случая самым безопасным выбором будет Redux Toolkit. Здесь много общих эффектов, правила постоянно меняются, а ошибки стоят денег. Предсказуемый поток полезнее, чем более лёгкий API.
Orders, refunds, roles и filters влияют друг на друга. Общие totals появляются в нескольких местах. Логика refunds должна иметь одно понятное место. Когда finance спрашивает: "Почему изменилась эта цифра?", команде нужен ответ, который можно отследить.
Zustand может подойти, если команда небольшая и правила всё ещё простые. С него легко начать, но как только политики refunds расползаются по многим actions store, код быстро становится рыхлым. Jotai приятен, когда состояние делится на множество маленьких независимых частей. В таком приложении это обычно недолго, потому что totals, permissions и rules для refunds связывают слишком многое.
Для такого приложения выбирайте инструмент, который помогает удерживать изменения под контролем. В админ-системе с деньгами, правами и общими отчётами Redux Toolkit обычно даёт меньше сюрпризов через шесть месяцев.
Ошибки, которые команды совершают при выборе
Команды часто выбирают библиотеку состояния так же, как выбирают UI kit: копируют то, что выглядит популярным, быстрым или знакомым по демо. Обычно это заканчивается плохо. Хороший выбор зависит не от трендов, а от того, как ваша команда уже работает.
Одна из частых ошибок — выбирать по популярности, а не по привычкам команды. Если в команде любят явные правила, чек-листы для code review и понятную историю изменений, слишком свободный store может превратить простые обновления в догадки. Если команда предпочитает быстрые локальные изменения и маленькие файлы, более тяжёлый подход может ощущаться как бумажная работа.
Некоторые несоответствия заметны быстро. Команды используют Zustand для pricing rules, permissions или refund logic, а потом не могут понять, как показать audit trail, почему значение изменилось. Команды помещают изолированное UI state в Redux Toolkit, хотя это всего лишь flag модального окна, выбранная вкладка или временный filter. Команды дробят Jotai-state на слишком много маленьких atoms, а потом тратят больше времени на отслеживание зависимостей, чем на разработку функций.
Ещё одна ошибка — смешивать reducers, direct setters и atoms в одном приложении до того, как команда договорится об основном стиле. Такой смешанный подход может работать, но только если всем понятно, где уместен каждый паттерн. Без этого правила каждая новая функция превращается в новый спор.
Представьте, что один разработчик хранит server cache и business rules в Redux Toolkit, другой — шаги формы в Zustand, а третий создаёт десятки Jotai atoms для одного и того же экрана. Приложение может работать, но отладка становится медленнее, онбординг — сложнее, а простые изменения занимают больше времени, чем должны.
Большинству команд лучше иметь один основной подход и короткий список исключений. Тогда review чище, а состояние проще читать.
Быстрые проверки перед решением
Прежде чем окончательно выбрать библиотеку, проверьте один реальный пользовательский сценарий на бумаге. Возьмите что-то сложное, например "вернуть заказ, обновить остатки, записать действие и обновить dashboard". Если товарищ по команде не может проследить этот поток за несколько минут, схема уже слишком умная.
Задайте четыре простых вопроса:
- Может ли новый член команды проследить одно действие пользователя от клика до изменения состояния, вызова API и обновления экрана?
- Может ли support повторить баг по заметке клиента, не гадая, какое скрытое состояние изменилось первым?
- Можно ли изменить одно бизнес-правило, например "менеджеры могут одобрять refunds до $500", не трогая пять файлов?
- Может ли один экран загрузиться и работать сам по себе, или он тихо зависит от состояния, которое раньше создал другой экран?
Эти проверки показывают разные слабые места. Redux Toolkit часто проходит тесты на прослеживаемость и повторяемость, потому что actions и reducers оставляют понятный след. Zustand кажется быстрее, когда поток простой, но слишком свободный store может размыть, где именно началось изменение. Jotai может держать экраны аккуратными, когда состояние разбивается на маленькие части, но слишком большое количество atoms усложняет понимание причины и следствия.
Плохой знак — когда ответ меняется в зависимости от того, кто объясняет код. Ещё один — когда небольшое изменение правила заставляет кого-то искать побочные эффекты по всему приложению. Эта цена обычно всплывает во время исправления багов и срочных релизов.
Если после такого теста два варианта всё ещё равны, выбирайте тот, с которым баги проще отслеживать, а правила — менять.
Что делать дальше
Перестаньте сравнивать библиотеки в вакууме. Соберите одну реальную функцию на вашем текущем фаворите и посмотрите, как команда справляется с ней под обычным давлением.
Выберите функцию с достаточной сложностью, чтобы она была честным тестом. Flow одобрения refunds, страница настроек с привязкой к ролям или экран массового редактирования подойдут хорошо, потому что там смешаны async requests, permissions, loading states и бизнес-правила.
Первый шаг должен быть узким. Не переносите всё приложение. Постройте новую модель состояния вокруг одного route, одной функции или одной ограниченной части интерфейса, чтобы протестировать её, не таща за собой весь остальной код.
Перед тем как писать код, сделайте короткую карту правил. Одной страницы достаточно. Запишите, что приходит с сервера, что нужно только для UI, кто может менять каждую часть состояния, какие действия запускают побочные эффекты или требуют audit trail, и какие правила меняются чаще всего.
Это маленькое упражнение снимает больше путаницы, чем ещё одна неделя споров о библиотеках. Когда правила видны, выбор обычно становится проще.
Через один-два спринта оцените результат простыми вопросами. Стало ли легче отслеживать баги? Быстрее ли новые члены команды находят логику? Остались ли изменения правил локальными или разошлись по всему приложению?
Если вы хотите получить внешнюю оценку до окончательного решения, Oleg Sotnikov делится опытом Fractional CTO и startup advisory на oleg.is и может помочь проверить React-архитектуру до того, как команда начнёт переписывание. Иногда точечный архитектурный разбор полезнее, чем полная миграция.
Небольшой пилот, письменная карта правил и честный обзор дадут вам больше, чем любая сравнительная таблица.
Часто задаваемые вопросы
Какой вариант лучше всего подходит, когда бизнес-правила постоянно меняются?
Выбирайте Redux Toolkit, когда одно действие пользователя затрагивает сразу несколько правил. Он хорошо подходит для приложений, где permissions, totals, audit logs и follow up requests должны оставаться синхронизированными.
Когда Zustand — лучший выбор?
Берите Zustand, когда маленькой команде нужно двигаться быстро, а поток действий легко объяснить. Он отлично подходит для filters, modal state, selected items, short lived drafts и UI state на уровне отдельных функций.
Какой тип React-экрана лучше всего подходит для Jotai?
Jotai хорошо работает на экранах с множеством небольших движущихся частей. Если tabs, drawers, inline forms, selected rows и несколько вычисляемых значений меняются на одной странице, atoms помогают держать эти части отдельно.
Нужен ли мне Redux Toolkit для простого UI state?
Обычно нет. Если вы храните только modal, tab или пару полей формы, Redux Toolkit часто добавляет больше структуры, чем нужно. Простое UI state лучше держать локально, пока от него не зависят несколько экранов.
Должны ли server data и UI state жить в одном store?
Держите их отдельно. Server data — это то, что вы запрашиваете и синхронизируете, например orders или users. UI state — это открытые панели, filters, draft text и selected rows. Если смешать их, разобраться в обновлениях станет сложнее.
Насколько сильно отладка должна влиять на выбор?
Это очень важно. Если support, QA или finance часто спрашивают, как изменилась value, Redux Toolkit даёт намного более понятный след. Если баги остаются локальными на одном экране, Zustand или Jotai тоже могут подойти.
Как понять, что Zustand-store стал слишком большим?
Следите за одним большим store, который хранит несвязанные вещи вроде orders, refunds, roles, loading flags и async actions. Когда изменение одного поля неожиданно затрагивает три экрана, store уже вышел за пределы простого подхода, из-за которого он был удобен.
Можно ли использовать Redux Toolkit, Zustand и Jotai в одном приложении?
Да, но сначала задайте один основной подход. Затем добавьте короткое правило для исключений, например local feature state в Zustand или screen level atoms в Jotai. Без такого правила каждая новая функция будет оформляться по-своему, а ревью замедлятся.
Как лучше всего проверить выбор перед миграцией?
Начните не с демо, а с одной болезненной production-функции. Соберите поток с async requests, derived values, permissions и несколькими недавними багами, а потом посмотрите, как быстро команда сможет отследить изменения и поправить правила через один-два спринта.
Какой инструмент обычно лучше всего подходит для админ-приложения с orders, refunds и roles?
Для такого приложения обычно лучше подходит Redux Toolkit. Orders, refunds, roles, totals и общие filters влияют друг на друга, а ошибки стоят денег. В такой ситуации предсказуемый поток обновлений полезнее, чем более лёгкий API.