React: Redux Toolkit
2026-02-26 17:23 Diff

Мы называем состоянием все, что находится в хранилище. При этом не все состояния одинаково полезны. Документация Redux предлагает такую классификацию:

  • Domain data — это данные, которые нужно отображать, использовать и модифицировать (например, загруженный с сервера список пользователей)
  • App state — данные, которые определяют поведение приложения (например, открытый URL)
  • UI state — данные, которые определяют внешний вид (например, вывод списка в виде плиток)

Хранилище — это ядро приложения, поэтому данные внутри него должны описываться в терминах domainData и appState, а не как дерево компонентов UI.

Например, способ формирования состояния state.leftPane.todoList.todos — плохая идея. Очень редко дерево компонентов отражается напрямую на структуру состояния, и это нормально. Представление зависит от данных, а не данные от представления.

Типичная структура состояния выглядит так:

Как уже говорилось в курсе JS: React, структура состояния должна напоминать базу данных. Все максимально плоско и нормализованно:

С такой структурой намного проще писать реакции на действия, добавлять новые данные и удалять старые. Вложенность небольшая, поэтому все просто. Но появляется другая проблема — чем больше сущностей, тем тяжелее становится редьюсер. Постепенно он превращается в огромный кусок кода, который делает все.

Чтобы решить эту проблему, можно использовать механизм, встроенный в Redux. Он позволяет создавать множественные редьюсеры и комбинировать их друг с другом. Работает это так:

  • Для каждого свойства верхнего уровня пишется свой редьюсер
  • С помощью функции combineReducers() все редьюсеры объединяются в корневой редьюсер (root)
  • Корневой редьюсер используется для создания хранилища

В итоге действия попадают во все редьюсеры, собранные внутри combineReducers():

В каждый редьюсер приходит state, но это не все состояние хранилища, а только та часть, которая лежит в соответствующем свойстве. В примере выше в редьюсер todosReducer будет передаваться состояние из свойства todos, а в commentsReducer из comments. Эта привязка происходит через объект, который передается в combineReducers():

Схематично комбинацию редьюсеров можно обозначить так:

А еще редьюсеры могут быть даже вложенными. Для этого не нужны никакие специальные средства — это обычные функции, принимающие одни данные и возвращающие другие данные.

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

Правильный подход в этом случае — повторять часть case в нужных редьюсерах.