React: Redux Toolkit
Теория: Подключение Redux Toolkit к React
Перейдем к Redux Toolkit и соберем простое приложение с несколькими кнопками — они будут менять значение счетчика. На этом примере мы увидим основные концепции Redux Toolkit. Для интеграции нам понадобятся два пакета:
- Пакет react-redux
- Сам Redux Toolkit из пакета @reduxjs/toolkit
Перейдем к установке:
Изучим структуру директорий, от которой будем отталкиваться. Это самый простой вариант, но не единственный возможный:
Для работы с Toolkit мы выделили директорию slices с двумя файлами внутри — index.js и counterSlice.js.
В файле index.js мы будем инициализировать хранилище и объединять все редьюсеры. Сами редьюсеры разбиваются по отдельным файлам — слайсам или срезам. Каждый слайс отвечает за определенную сущность в приложении. Например, в приложении со списком товаров и покупателей можно выделить два слайса: один для товаров, другой для покупателей.
Такой подход разделяет общее состояние на отдельные модули со своей зоной ответственности. В нашем примере в состоянии находится счетчик, поэтому мы определили для него слайс counterSlice.js. Теперь создаем редьюсер в слайсе для счетчика:
Чтобы создать редьюсер, создаем начальное значение initialState и вызываем createSlice(), который выполняет всю работу. Эта функция принимает объект, в котором нам важны три свойства:
nameзадает имя слайсаinitialStateзадает начальное состояниеreducersпринимает объект, в котором каждое свойство содержит редьюсеры. С их помощью мы будем менять состояние
Вызов createSlice() вернет готовый слайс — это объект, в котором нам важны два свойства:
-
actions— это действия, с помощью которых мы запускаем созданные редьюсеры. Названия действий совпадают с ключами, которые мы указали вreducersпри создании слайса. Toolkit автоматически создаст нужные действия и даст строковые имена их типам.В примере выше мы экспортируем объект с действиями, которые получили из этого свойства. Дальше можно импортировать действия в компонентах, чтобы вызывать их.
-
reducer— это готовый редьюсер, который мы будем подключать в хранилище. В примере выше он экспортируется по умолчанию просто для удобства, чтобы разграничить экспорт экшенов и редьюсера.
Теперь редьюсер готов к использованию. Сначала подключим его в общее хранилище. Для этого передаем редьюсер в функцию configureStore().
Эта функция умеет комбинировать редьюсеры самостоятельно, в отличие от такой же функции в Redux. Функция configureStore() принимает на вход объект с ключом reducer, значением которого становится объект с редьюсерами. У общего состояния state ключи будут такими же, как у этого объекта. Более подробно мы это разберем чуть позже. Создание хранилища выглядит так:
Здесь мы вызываем функцию configureStore() и передаем в нее объект со свойством reducer. А вот уже в reducer мы указываем объект с нашими редьюсерами. В нашем примере есть единственный редьюсер counterReducer, который мы импортируем по умолчанию из counterSlice.js.
Если бы у нас было несколько слайсов, можно было бы указать их под разными ключами. Это выглядело бы так:
Выше мы видим два редьюсера, которые передаются под ключами users и tasks. Мы можем придумывать любые имена ключей, но лучше, когда имена соответствуют содержимому. Например, редьюсер для списка пользователей лучше назвать users, а для списка задач — tasks.
Теперь наше хранилище готово к использованию. Можно подключить его в приложение.
Начнем создавать приложение с верхнего уровня. Здесь понадобится компонент <Provider>, который содержит хранилище и прокидывает его вглубь дерева компонентов через контекст:
Теперь самое главное — Toolkit в действии. Здесь мы опираемся на работу хуков. Здесь все работает по-прежнему:
- Чтобы изменить состояние в хранилище, передаем действие в функцию
dispatch() - Чтобы получить объект
dispatchв компоненте, используем функциюuseDispatch() - Чтобы извлечь данные из стора, используем хук
useSelector(). Он принимает функцию, в которую все состояние передается через параметр. Возвращаемое значение из этой функции станет результатом выполненияuseSelector()
Посмотрим, как это работает:
В компоненте может быть несколько вызовов useSelector, причем каждый вызов создаст подписку на изменение состояния. Срабатывание нескольких подписок одновременно приведет только к одной перерисовке компонента.
Обратите внимание, что переданная в useSelector функция принимает все состояние целиком. Если у нас несколько редьюсеров и слайсов, состояние содержит все состояния этих слайсов. Состояние хранится в объекте, где каждый ключ — это то, что мы указали в reducer при создании стора. В нашем случае это свойство counter:
Рассмотрим интерактивный пример:
https://codepen.io/hexlet/pen/bGodExN
Часть этой инициализации делается один раз и почти не меняется. Основной код приложения будет добавляться в компонентах и слайсах. Благодаря слайсам и мутации данных внутри редьюсеров, кода будет значительно меньше, чем в чистом Redux. Об этом мы подробнее поговорим в следующем уроке.
Осталось разобрать подключение мидлвар:
Мидлвары подключаются через свойство middleware. В это свойство мы записываем функцию, которая в свою очередь принимает другую функцию getDefaultMiddleware(). Вызвав ее, мы получаем список текущих мидлвар. К этим мидлварам добавляем наши мидлвары и возвращаем новый список.


