При использовании библиотеки Redux есть три основные проблемы.
Сложный процесс настройки Redux-хранилища
Необходимость добавлять стандартный набор дополнительных пакетов
Большой объем бойлерплейт кода
это официальная библиотека для эффективной разработки с использованием Redux, которая предназначена для стандартизации написания Redux-логики.
Позволяет сосредоточиться на написании основной логики приложения, не тратя время на настройку.
Включает в себя утилиты для упрощения основных задач. Таких как настройка хранилища, создание редюсеров, иммутабельное обновление данных и многое другое.
Предоставляет стандартный набор настроек для хранилища и включает в себя наиболее часто используемые дополнения из Redux-экосистемы.
Библиотека не предназначена для решения всех возможных проблем и намеренно ограничена в объеме. Такие решения как HTTP-запросы, структура папок и файлов, управление связями сущностей в хранилище и т. д. ложаться на плечи разработчика. Тем не менее, Redux Toolkit будет полезен для всех стандартных задач, поможет упростить и улучшить Redux-код.
2. Использование Redux Toolkit
Возьмем готовую Redux-логику таймера и провередм рефакторинг используя Redux Toolkit.
import { createStore, combineReducers } from 'redux';
// Action types
const INCREMENT = 'timer/increment';
const DECREMENT = 'timer/decrement';
// Action creators
function increment(value) {
return {
type: INCREMENT,
payload: value,
};
}
function decrement(value) {
return {
type: DECREMENT,
payload: value,
};
}
// Reducer
function timer(state = 0, action) {
switch (action.type) {
case INCREMENT:
return state + action.payload;
case DECREMENT:
return state - action.payload;
default:
return state;
}
}
const rootReducer = combineReducers({
timer,
});
// Store
const store = createStore(rootReducer);
2.1. Установка
Библиотека доступна как стандартный NPM-пакет.
npm install @reduxjs/toolkit
2.2. Функия configureStore()
Обычно хранилище создается вызовом createStore(), в который передается корневой редюсер. Redux Toolkit содержит функцию configureStore(), которая оборачивает оригинальный createStore(), и настраивает некоторые полезные инструменты разработки как часть процесса создания хранилища.
Заменим вызов createStore() на configureStore(), который ожидает один аргумент - объект с набором строго именованных свойств.
// До
import { createStore } from 'redux';
const store = createStore(timer);
// После
import { combineReducers } from 'redux';
import { configureStore } from '@reduxjs/toolkit';
const rootReducer = combineReducers({
timer,
});
const store = configureStore({
reducer: rootReducer,
});
На первый вгляд практически одно и тоже, тем не менее, после рефакторинга, под капотом сразу были настроены инструменты разработчика (Redux DevTools) и некоторые другие полезные функции.
Можно передать больше одного редюсера, и configureStore() сам создаст корневой редюсер. Для этого в свойство reducer передается объект.
import { configureStore } from '@reduxjs/toolkit';
const store = configureStore({
reducer: {
timer,
},
});
Теперь Redux-код таймера выглядит следующим образом.
import { configureStore, combineReducers } from '@reduxjs/toolkit';
// Action types
const INCREMENT = 'timer/increment';
const DECREMENT = 'timer/decrement';
// Action creators
function increment(value) {
return {
type: INCREMENT,
payload: value,
};
}
function decrement(value) {
return {
type: DECREMENT,
payload: value,
};
}
// Reducer
function timer(state = 0, action) {
switch (action.type) {
case INCREMENT:
return state + action.payload;
case DECREMENT:
return state - action.payload;
default:
return state;
}
}
// Store
const store = configureStore({
reducer: {
timer,
},
});
Copy
2.3. Функция createAction()
Функция createAction() в качестве аргумента принимает строку описывающую тип действия и возвращает action creator.
Продолжим рефакторить Redux-код таймера. Использование createAction() избавит нас от нескольких строк кода.
import { configureStore, createAction } from '@reduxjs/toolkit';
// Action creators
const increment = createAction('timer/increment');
const decrement = createAction('timer/decrement');
// Reducer
function timer(state = 0, action) {
switch (action.type) {
case INCREMENT:
return state + action.payload;
case DECREMENT:
return state - action.payload;
default:
return state;
}
}
// Store
const store = configureStore({
reducer: {
timer,
},
});
2.4. Функция createReducer()
Наиболее распространенным подходом создания редюсера является проверка свойства action.type внутри switch и выполнение определенной логики для каждого типа действия. К тому же редюсер определяет начальное значение состояния и возвращает полученное состояние, если он не должен обрабатывать дейтсвие такого типа.
Используя функцию createReducer() можно создавать редюсеры передав объект свойств специального формата, где каждый ключ это тип действия, а значение - это редюсер для этого типа.
Так как синтаксис вычисляемых свойств объекта вызывает метод toString() указанной переменной, можно просто использовать имя функции без указания свойства type, ведь метод toString() наших action creators был переопределен так, чтобы возвращать тип дейсвтвия.
const timer = createReducer(0, {
[increment]: (state, action) => state + action.payload,
[decrement]: (state, action) => state - action.payload,
});