Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Редьюсеры JS: Redux (React)

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

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

Так как стор представляет собой ядро приложения, данные внутри него должны описываться в терминах domain data и app state, но не как дерево компонентов UI. Например, такой способ формирования состояния state.leftPane.todoList.todos — плохая идея. Крайне редко дерево компонентов отражается напрямую на структуру состояния, и это нормально. Представление зависит от данных, а не данные от представления.

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

{
    domainData1 : {}, // todos
    domainData2 : {}, // comments
    appState1 : {},
    appState2 : {},
    uiState1 : {},
    uiState2 : {},
}

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

{
  todos: [
    { id: 1, name: 'why?' },
    { id: 3, name: 'who?' },
  ],
  comments: [
    { id: 23, todoId: 3, text: 'great!' },
  ],
}

С такой структурой крайне легко писать реакцию на действия, обновлять данные, добавлять новые и удалять старые. Вложенность небольшая, всё просто. Но появляется другая проблема (появляется она в любом случае). С ростом количества сущностей, редьюсер становится очень тяжёлым. Огромный кусок кода, который делает всё. Для решения этой проблемы, Redux имеет встроенный механизм, позволяющий создавать множественные редьюсеры и комбинировать их друг с другом. Работает это так: для каждого свойства верхнего уровня пишется свой собственный редьюсер, а затем они с помощью функции combineReducers() объединяются в корневой (root) редьюсер, который уже используется для создания хранилища. Экшены попадают во все редьюсеры, собранные внутри combineReducers().

import { combineReducers, createStore } from 'redux';

const todosReducer = (state = [], action) => {
  // сюда попадут данные из todos
};

const commentsReducer = (state = [], action) => {
  // сюда попадут данные из comments
};

const rootReducer = combineReducers({
  todos: todosReducer,
  comments: commentsReducer,
});
const store = createStore(rootReducer);

// Если назвать редьюсеры как свойства в состоянии, то код можно сократить:
// const todos = (state = [], action) => { ... };
// const comments = (state = [], action) => { ... };
// const rootReducer = combineReducers({ todos, comments });

В каждый редьюсер приходит state, но это не всё состояние хранилища, а только та часть, которая лежит в соответствующем свойстве. Не забудьте про это.

Комбинация редьюсеров Redux

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

const todos = (state = {}, action) => {
  switch (action.type) {
    case 'TODO_REMOVE':
      // ...
  }
};

const comments = (state = {}, action) => {
  switch (action.type) {
    // При удалении ToDo нужно удалить все его комментарии
    case 'TODO_REMOVE':
      // ...
  }
};

То есть правильный подход состоит в том, чтобы повторять часть case в нужных редьюсерах, а не в том, чтобы пытаться получить недостающие части состояния.


Дополнительные материалы

  1. Нормализация структуры состояния

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 6 300 ₽ в месяц
Разработка фронтенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 1 июня
профессия
от 10 080 ₽ в месяц
Разработка фронтенд- и бэкенд-компонентов для веб-приложений
16 месяцев
с нуля
Старт 1 июня

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»