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

Начало работы с Redux

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

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

Redux — это JS-библиотека для предсказуемого и поддерживаемого управления глобальным состоянием.

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

Redux можно использовать с React или любой другой UI-библиотекой. Библиотека компактна (2kB включая зависимости) и имеет богатую экосистему дополнений.

Redux Toolkit (RTK) — наш официальный рекомендуемый подход для написания Redux-логики. Это надстройка над ядром Redux, содержащая пакеты и функции, необходимые для создания Redux-приложений. RTK воплощает лучшие практики, упрощает большинство задач, предотвращает типичные ошибки и облегчает разработку на Redux.

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

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

Установка

Redux Toolkit

Redux Toolkit доступен как пакет на NPM для использования со сборщиком модулей или в Node-приложении:

# NPM
npm install @reduxjs/toolkit

# Yarn
yarn add @reduxjs/toolkit

Создание нового Redux-проекта

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

Для создания проекта можно использовать инструмент вроде tiged, чтобы клонировать и распаковать шаблон.

# Vite + TypeScript
npx tiged reduxjs/redux-templates/packages/vite-template-redux my-app

# Create React App + TypeScript
npx tiged reduxjs/redux-templates/packages/cra-template-redux-typescript my-app

# Create React App + JavaScript
npx tiged reduxjs/redux-templates/packages/cra-template-redux my-app

# Expo + TypeScript
npx tiged reduxjs/redux-templates/packages/expo-template-redux-typescript my-app

# React Native + TypeScript
npx tiged reduxjs/redux-templates/packages/react-native-template-redux-typescript my-app

# Standalone Redux Toolkit App Structure Example
npx tiged reduxjs/redux-templates/packages/rtk-app-structure-example my-app

Помимо официальных шаблонов, сообщество создало другие варианты, например шаблон Next.js with-redux.

# Next.js + Redux
npx create-next-app --example with-redux my-app

Ядро Redux

Ядро Redux доступно как пакет на NPM для использования со сборщиком модулей или в Node-приложении:

# NPM
npm install redux

# Yarn
yarn add redux

Пакет включает предварительно скомпилированную ESM-сборку, которую можно использовать как <script type="module"> прямо в браузере.

Подробнее см. на странице Установка.

Базовый пример

Всё глобальное состояние вашего приложения хранится в виде дерева объектов внутри одного хранилища (store). Единственный способ изменить это состояние — создать действие (action), объект описывающий произошедшее, и отправить (dispatch) его в хранилище. Чтобы определить как состояние обновляется в ответ на действие, вы пишете чистые редьюсеры (reducers), вычисляющие новое состояние на основе предыдущего состояния и полученного действия.

Redux Toolkit упрощает процесс написания Redux-логики и настройки хранилища. С использованием RTK базовая логика приложения выглядит так:

import { createSlice, configureStore } from '@reduxjs/toolkit'

const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0
},
reducers: {
incremented: state => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1
},
decremented: state => {
state.value -= 1
}
}
})

export const { incremented, decremented } = counterSlice.actions

const store = configureStore({
reducer: counterSlice.reducer
})

// Can still subscribe to the store
store.subscribe(() => console.log(store.getState()))

// Still pass action objects to `dispatch`, but they're created for us
store.dispatch(incremented())
// {value: 1}
store.dispatch(incremented())
// {value: 2}
store.dispatch(decremented())
// {value: 1}

Вместо прямого изменения состояния вы описываете изменения с помощью простых объектов, называемых действиями (actions). Затем вы пишете специальную функцию — редюсер (reducer), которая определяет, как каждое действие преобразует всё состояние приложения.

В типичном Redux-приложении существует единственное хранилище (store) с одной корневой функцией-редюсером. По мере роста приложения корневой редюсер разделяется на более мелкие редюсеры, которые независимо работают с разными частями дерева состояния. Это аналогично тому, как в React-приложении есть один корневой компонент, состоящий из множества мелких компонентов.

Такая архитектура может показаться избыточной для счётчика, но её прелесть — в отличной масштабируемости для больших и сложных приложений. Она также позволяет использовать мощные инструменты разработчика, так как каждое изменение можно отследить до вызвавшего его действия. Вы можете записывать пользовательские сессии и воспроизводить их, просто повторяя каждое действие.

Redux Toolkit позволяет писать более лаконичный и читаемый код, сохраняя при этом стандартное поведение Redux и поток данных.

Устаревший пример

Для сравнения, оригинальный устаревший синтаксис Redux (без абстракций) выглядит так:

import { createStore } from 'redux'

/**
* This is a reducer - a function that takes a current state value and an
* action object describing "what happened", and returns a new state value.
* A reducer's function signature is: (state, action) => newState
*
* The Redux state should contain only plain JS objects, arrays, and primitives.
* The root state value is usually an object. It's important that you should
* not mutate the state object, but return a new object if the state changes.
*
* You can use any conditional logic you want in a reducer. In this example,
* we use a switch statement, but it's not required.
*/
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 }
case 'counter/decremented':
return { value: state.value - 1 }
default:
return state
}
}

// Create a Redux store holding the state of your app.
// Its API is { subscribe, dispatch, getState }.
let store = createStore(counterReducer)

// You can use subscribe() to update the UI in response to state changes.
// Normally you'd use a view binding library (e.g. React Redux) rather than subscribe() directly.
// There may be additional use cases where it's helpful to subscribe as well.

store.subscribe(() => console.log(store.getState()))

// The only way to mutate the internal state is to dispatch an action.
// The actions can be serialized, logged or stored and later replayed.
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}

Изучение Redux

У нас есть различные ресурсы для изучения Redux.

Учебник Redux Essentials

Учебник Redux Essentials — это учебник «сверху вниз», который учит «правильному использованию Redux» с применением новейших рекомендуемых API и лучших практик. Мы рекомендуем начать с него.

Учебник Redux Fundamentals

Учебник Redux Fundamentals — это учебник «снизу вверх», который объясняет «как работает Redux» на базовом уровне без абстракций и показывает причины появления стандартных паттернов использования.

Livestream «Изучение современного Redux»

Мейнтейнер Redux Марк Эриксон выступил в шоу «Learn with Jason», объясняя современные подходы к использованию Redux. В эфире было показано живое создание приложения с использованием Redux Toolkit, хуков React-Redux с TypeScript и новых API для запросов данных RTK Query.

Подробности смотрите на странице заметок шоу «Learn Modern Redux», включая расшифровку и ссылки на исходный код примера.

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

Другие ресурсы

Помощь и обсуждение

Официальным ресурсом для вопросов по изучению и использованию Redux является канал #redux в сообществе Reactiflux Discord. Reactiflux — отличное место для общения, вопросов и обучения. Присоединяйтесь!

Также вы можете задавать вопросы на Stack Overflow с тегом #redux.

Для сообщений об ошибках и обратной связи создайте issue в репозитории GitHub.

Нужен ли вам Redux?

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

Вот ситуации, где Redux особенно полезен:

  • У вас часто изменяются значительные объёмы данных

  • Требуется единый источник истины для состояния приложения

  • Хранение всего состояния в верхнеуровневом компоненте стало неэффективно

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