React course
  • Компоненты и коллекции
  • TypeScript
  • Стилизация
  • События и состояния
  • Формы
  • Жизненный цикл
  • Функциональные vs классовые компоненты
  • Основы Redux
  • Redux Toolkit
  • Асинхронный Redux
  • Селекторы
  • React Router
  • Code splitting
  • Паттерны и контекст
  • Анимация
Powered by GitBook
On this page
  • 1. Redux Toolkit
  • 2. Использование Redux Toolkit
  • 2.1. Установка
  • 2.2. Функия configureStore()
  • 2.3. Функция createAction()
  • 2.4. Функция createReducer()

Was this helpful?

Redux Toolkit

PreviousОсновы ReduxNextАсинхронный Redux

Last updated 4 years ago

Was this helpful?

1. Redux Toolkit

При использовании библиотеки 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.

// До
const INCREMENT = 'timer/increment';

function increment(value) {
  return {
    type: INCREMENT,
    payload: value,
  };
}
console.log(increment(5)); // {type: "timer/increment", payload: 5}

// После
import { createAction } from '@reduxjs/toolkit';

const increment = createAction('timer/increment');
console.log(increment(5)); // {type: "timer/increment", payload: 5}
Copy

А где взять тип действия, например для использования внутри редюсера? Есть два способа.

  • Метод toString() функции increment был переопределен и возвращает строку типа действия.

  • Значение типа действия можно получить обратившись к свойству type функции increment

const increment = createAction('INCREMENT');

console.log(increment.toString()); // "timer/increment"
console.log(increment.type); // "timer/increment"

Продолжим рефакторить 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() можно создавать редюсеры передав объект свойств специального формата, где каждый ключ это тип действия, а значение - это редюсер для этого типа.

import { createReducer } from '@reduxjs/toolkit';

const increment = createAction('timer/increment');
const decrement = createAction('timer/decrement');

const timer = createReducer(0, {
  [increment.type]: (state, action) => state + action.payload,
  [decrement.type]: (state, action) => state - action.payload,
});

Так как синтаксис вычисляемых свойств объекта вызывает метод toString() указанной переменной, можно просто использовать имя функции без указания свойства type, ведь метод toString() наших action creators был переопределен так, чтобы возвращать тип дейсвтвия.

const timer = createReducer(0, {
  [increment]: (state, action) => state + action.payload,
  [decrement]: (state, action) => state - action.payload,
});

Применим этот синтаксис к коду таймера.

import { configureStore, createAction, createReducer } from '@reduxjs/toolkit';

// Action creators
const increment = createAction('timer/increment');
const decrement = createAction('timer/decrement');

// Reducer
const timer = createReducer(0, {
  [increment]: (state, action) => state + action.payload,
  [decrement]: (state, action) => state - action.payload,
});

// Store
const store = configureStore({
  reducer: {
    timer,
  },
});

Redux Toolkit
Документация Redux Toolkit
Документация configureStore()
Документация createAction()
Документация createReducer()