Перейти к основному содержимому

Почему Redux Toolkit — это современный способ использования Redux

Неофициальный Бета-перевод

Эта страница переведена PageTurner AI (бета). Не одобрена официально проектом. Нашли ошибку? Сообщить о проблеме →

Что такое Redux Toolkit?

Redux Toolkit (также известный как "RTK") — это наш официально рекомендуемый подход для написания логики Redux. Пакет @reduxjs/toolkit оборачивает основную библиотеку redux и содержит API-методы и общие зависимости, которые мы считаем необходимыми для создания Redux-приложений. Redux Toolkit включает наши рекомендуемые лучшие практики, упрощает большинство задач Redux, предотвращает распространённые ошибки и облегчает написание Redux-приложений.

Если вы пишете любую логику Redux сегодня, вы должны использовать Redux Toolkit для написания этого кода!

RTK включает утилиты, которые помогают упростить многие распространённые сценарии, такие как настройка хранилища, создание редюсеров и написание логики неизменяемых обновлений, и даже создание целых "срезов" (slices) состояния.

Независимо от того, являетесь ли вы новым пользователем Redux, настраивающим свой первый проект, или опытным разработчиком, который хочет упростить существующее приложение, Redux Toolkit поможет вам сделать ваш Redux-код лучше.

Совет

Ознакомьтесь с этими материалами, чтобы узнать, как использовать "современный Redux" с Redux Toolkit:

Чем Redux Toolkit отличается от ядра Redux

Что такое "Redux"?

Первым делом стоит спросить: "что такое Redux?"

По сути, Redux — это:

  • Единое хранилище, содержащее "глобальное" состояние

  • Отправка простых объектов-действий (actions) в хранилище при возникновении событий в приложении

  • Чистые функции-редюсеры, которые обрабатывают эти действия и возвращают неизменяемо обновлённое состояние

Хотя это не обязательно, ваш Redux-код обычно также включает:

  • Создателей действий (action creators), которые генерируют эти объекты-действия

  • Промежуточное ПО (middleware) для обработки побочных эффектов

  • Функции-санки (thunks), содержащие синхронную или асинхронную логику с побочными эффектами

  • Нормализованное состояние для поиска элементов по ID

  • Мемоизированные функции-селекторы с библиотекой Reselect для оптимизации производных данных

  • Расширение Redux DevTools для просмотра истории действий и изменений состояния

  • TypeScript-типы для действий, состояния и других функций

Кроме того, Redux обычно используется с библиотекой React-Redux для взаимодействия React-компонентов с Redux-хранилищем.

Что предоставляет ядро Redux?

Ядро Redux — это очень небольшая и намеренно не навязывающая мнений библиотека. Она предоставляет несколько простых API-примитивов:

  • createStore для создания Redux-хранилища

  • combineReducers для объединения нескольких редюсеров в один корневой редюсер

  • applyMiddleware для комбинирования нескольких промежуточных слоёв в усилитель хранилища (store enhancer)

  • compose для объединения нескольких усилителей хранилища в один

Всю остальную Redux-логику в вашем приложении вам придётся писать самостоятельно.

Хорошая новость: это означает, что Redux можно использовать разными способами. Плохая новость: нет вспомогательных инструментов для упрощения кода.

Например, функция-редюсер — это просто функция. До появления Redux Toolkit вы обычно писали такой редюсер с помощью оператора switch и ручных обновлений. В дополнение к этому вы бы создавали вручную создателей действий и константы типов действий:

Legacy hand-written Redux usage
const ADD_TODO = 'ADD_TODO'
const TODO_TOGGLED = 'TODO_TOGGLED'

export const addTodo = text => ({
type: ADD_TODO,
payload: { text, id: nanoid() }
})

export const todoToggled = id => ({
type: TODO_TOGGLED,
payload: id
})

export const todosReducer = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return state.concat({
id: action.payload.id,
text: action.payload.text,
completed: false
})
case TODO_TOGGLED:
return state.map(todo => {
if (todo.id !== action.payload) return todo

return {
...todo,
completed: !todo.completed
}
})
default:
return state
}
}

Ни один из этих фрагментов кода не зависит от API ядра redux. Но это очень много кода. Неизменяемые обновления требовали множества ручных операций с объектами и массивами, и было легко допустить ошибки и случайно изменить состояние (что всегда было причиной №1 ошибок в Redux!). Также было распространено (хотя и не обязательно) разделение кода для одной функциональности между несколькими файлами: actions/todos.js, constants/todos.js и reducers/todos.js.

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

Что делает Redux Toolkit?

Хотя это были шаблоны, изначально показанные в документации Redux, к сожалению, они требовали много многословного и повторяющегося кода. Большая часть этого шаблонного кода не обязательна для использования Redux. Более того, такой код создавал больше возможностей для ошибок.

Мы специально создали Redux Toolkit, чтобы устранить "шаблонность" рукописной Redux-логики, предотвратить распространённые ошибки и предоставить API для упрощения стандартных задач Redux.

Redux Toolkit начинается с двух ключевых API, которые упрощают самые распространённые операции в Redux-приложениях:

  • configureStore настраивает правильно сконфигурированное Redux-хранилище одним вызовом функции, включая объединение редюсеров, добавление промежуточного слоя для санков и настройку интеграции с Redux DevTools. Его также проще настроить, чем createStore, поскольку он принимает именованные параметры.

  • createSlice позволяет писать редюсеры с использованием библиотеки Immer, что позволяет писать неизменяемые обновления с использованием "мутирующего" синтаксиса JS, такого как state.value = 123, без операторов распространения (spreads). Он также автоматически генерирует функции-создатели действий для каждого редюсера и внутренне создаёт строки типов действий на основе имён редюсеров. Кроме того, он отлично работает с TypeScript.

Это означает, что ваш код может стать значительно проще. Например, тот же редюсер для задач (todos) может выглядеть так:

features/todos/todosSlice.js
import { createSlice } from '@reduxjs/toolkit'

const todosSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
todoAdded(state, action) {
state.push({
id: action.payload.id,
text: action.payload.text,
completed: false
})
},
todoToggled(state, action) {
const todo = state.find(todo => todo.id === action.payload)
todo.completed = !todo.completed
}
}
})

export const { todoAdded, todoToggled } = todosSlice.actions
export default todosSlice.reducer

Все создатели действий и типы действий генерируются автоматически, а код редюсера становится короче и понятнее. Также гораздо яснее, что именно обновляется в каждом случае.

С configureStore настройка хранилища упрощается до:

app/store.js
import { configureStore } from '@reduxjs/toolkit'
import todosReducer from '../features/todos/todosSlice'
import filtersReducer from '../features/filters/filtersSlice'

export const store = configureStore({
reducer: {
todos: todosReducer,
filters: filtersReducer
}
})

Обратите внимание, что один вызов configureStore автоматически выполняет всю стандартную ручную настройку:

  • Срезовые редюсеры автоматически передаются в combineReducers()

  • Промежуточный слой redux-thunk автоматически добавляется

  • Добавляется промежуточный слой для разработки, предотвращающий случайные мутации

  • Автоматически настраивается расширение Redux DevTools

  • Промежуточные слои и усилители DevTools комбинируются и добавляются в хранилище

При этом configureStore предоставляет опции для изменения любого из этих стандартных поведений (например, отключение санков и добавление саг, или отключение DevTools в продакшене).

Помимо этого, Redux Toolkit включает другие API для распространённых задач Redux:

  • createAsyncThunk: абстрагирует стандартный шаблон "отправка действий до/после асинхронного запроса"

  • createEntityAdapter: предопределенные редюсеры и селекторы для CRUD-операций с нормализованным состоянием

  • createSelector: реэкспорт стандартного API Reselect для мемоизированных селекторов

  • createListenerMiddleware: middleware для побочных эффектов, запускающий логику в ответ на диспатч экшенов

Наконец, пакет RTK также включает "RTK Query" — готовое решение для получения данных и кэширования в Redux-приложениях. Оно доступно как отдельная опциональная точка входа @reduxjs/toolkit/query. RTK Query позволяет определять конечные точки (REST, GraphQL или любые асинхронные функции) и автоматически генерирует редюсеры и middleware, которые полностью управляют получением данных, обновлением состояния загрузки и кэшированием результатов. Также автоматически генерируются React-хуки для использования в компонентах, например: const { data, isFetching } = useGetPokemonQuery('pikachu')

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

Важно: Redux Toolkit — это всё ещё "Redux"! По-прежнему существует единое хранилище (store), обновления через диспатч объектов-экшенов, редюсеры для иммутабельного обновления состояния. Сохраняются возможности написания thunk'ов для асинхронной логики, управления нормализованным состоянием, типизации кода с TypeScript и использования DevTools. Просто вам нужно писать значительно меньше кода для тех же результатов!

Почему мы рекомендуем использовать Redux Toolkit

Как мейнтейнеры Redux, мы считаем:

Совет

Мы хотим, чтобы все пользователи Redux писали свой код с помощью Redux Toolkit, потому что это упрощает ваш код и устраняет множество распространённых ошибок и багов в Redux!

"Шаблонность" и сложность ранних паттернов Redux никогда не были неотъемлемой частью Redux. Эти паттерны возникли только потому что:

  • Исходная "Flux Architecture" использовала похожие подходы

  • Ранняя документация Redux демонстрировала такие вещи, как константы типов экшенов для разделения кода по файлам

  • JavaScript по умолчанию использует мутабельные структуры данных, поэтому иммутабельные обновления требовали ручного копирования объектов и массивов

  • Redux изначально был создан за несколько недель и намеренно спроектирован как минимальный набор примитивов API

Дополнительно сообщество Redux приняло специфические подходы, добавляющие шаблонности:

  • Акцент на использовании middleware redux-saga как стандартного подхода для побочных эффектов

  • Требование ручного написания TS-типов для объектов-экшенов и создания union-типов для ограничения диспатча на уровне типов

За годы мы увидели, как люди реально используют Redux на практике. Мы наблюдали, как сообщество создало сотни дополнительных библиотек для генерации типов и создателей экшенов, асинхронной логики, побочных эффектов и получения данных. Мы также видели проблемы, которые постоянно вызывают боль у пользователей: случайные мутации состояния, десятки строк кода для простого обновления состояния, сложности в понимании архитектуры кодовой базы. Мы помогли тысячам пользователей, которые пытались изучить и использовать Redux, но испытывали трудности с пониманием взаимодействия частей и были сбиты с толку количеством концепций и объёмом дополнительного кода. Мы знаем проблемы наших пользователей.

Мы специально разработали Redux Toolkit для решения этих проблем!

  • Redux Toolkit упрощает настройку хранилища до одного понятного вызова функции, сохраняя возможность полной конфигурации при необходимости

  • Redux Toolkit исключает случайные мутации — главную причину багов в Redux

  • Redux Toolkit устраняет необходимость ручного написания создателей экшенов и их типов

  • Redux Toolkit избавляет от ручного написания подверженной ошибкам логики иммутабельных обновлений

  • Redux Toolkit позволяет писать код Redux-фичи в одном файле вместо распределения по нескольким файлам

Redux Toolkit обеспечивает превосходную поддержку TypeScript: его API спроектированы для максимальной типобезопасности и минимизации ручного описания типов в вашем коде.

RTK Query полностью устраняет необходимость писать thunks, редьюсеры, создатели действий или хуки эффектов для управления загрузкой данных и отслеживания состояния запросов.

Поэтому:

Совет

Мы настоятельно рекомендуем разработчикам использовать Redux Toolkit (пакет @reduxjs/toolkit) и не использовать устаревший пакет redux для любого нового кода на Redux!

Даже в существующих приложениях мы рекомендуем как минимум заменить createStore на configureStore — встроенные middleware в режиме разработки помогут выявить случайные мутации и проблемы с сериализуемостью. Также советуем перевести наиболее используемые редьюсеры (и все новые) на createSlice — код станет короче и понятнее, а встроенные проверки сэкономят ваше время в будущем.

Пакет redux по-прежнему работает, но сейчас мы считаем его устаревшим. Все его API реэкспортируются из @reduxjs/toolkit, а configureStore полностью заменяет createStore с улучшенными настройками по умолчанию.

Понимание низкоуровневых концепций полезно для осознания того, что делает для вас Redux Toolkit. Поэтому в туториале «Redux Fundamentals» показана работа Redux без абстракций. Но эти примеры служат исключительно обучающим целям, а в завершение демонстрируется, как Redux Toolkit упрощает старый «ручной» код.

Код с использованием redux продолжит работать. Однако мы настоятельно рекомендуем перейти на @reduxjs/toolkit и обновить код для использования API Redux Toolkit!

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

Подробнее в документации и блогах: