Гид по технологиям

Руководство по Redux для React

9 min read Frontend Обновлено 29 Oct 2025
Руководство по Redux для React
Руководство по Redux для React

Логотип Redux

Быстрые ссылки

  • Что делает Redux?
  • Структура проекта
  • Установка и настройка Redux
  • Паттерны и лучшие практики
  • Альтернативы и когда не стоит использовать Redux

Что делает Redux?

Проще говоря, Redux — это централизованное хранилище данных. Вся прикладная информация хранится в одном большом объекте состояния. Для визуализации удобно использовать Redux DevTools:

Визуализация хранилища состояния в Redux

Ключевые идеи:

  • Состояние иммутабельно. Чтобы изменить состояние, нужно отправить действие (action).
  • Action описывает намерение изменить состояние (тип и полезная нагрузка — payload).
  • Reducer берёт текущее состояние и action, и возвращает новое состояние.
  • История действий сохраняется, что позволяет откатываться назад и отлаживать последовательности изменений (time-travel debugging).

Журнал действий (actions) в Redux

Redux можно использовать с любым фронтенд-фреймворком, но чаще всего — с React. Под капотом React + Redux используют механизм контекста, поэтому для простых приложений Context API может быть достаточен. Однако DevTools, оптимизации и зрелая экосистема делают Redux удобным выбором для сложных проектов.

Важно: в TypeScript настройка типизации для Redux требует дополнительной дисциплины. Для удобства часто используют библиотеки, такие как typesafe-actions или Redux Toolkit, которые упрощают строго типизированные actions и reducers.

Ментальная модель Redux

Короткая дефиниция терминов:

  • State: единый объект с текущими данными приложения.
  • Action: объект с обязательным полем type, описывающий событие.
  • Reducer: чистая функция (state, action) => newState.
  • Store: контейнер, который хранит state и предоставляет методы dispatch и subscribe.

Представьте Redux как четкий контракт по изменению данных: все изменения — через actions, reducers предсказуемо возвращают новый state, а store — единая точка доступа.

Структурирование проекта

Правильная структура кода снижает временные и когнитивные издержки при масштабировании. Ниже — часто используемые паттерны и примеры.

Паттерн “по типам файлов”

Разделение по типу файлов (actions, reducers, middleware) выглядит так:

store/

actions/

reducers/

sagas/

middleware/

index.js

Этот подход логичен, но в крупных проектах приводит к частым перекрёстным импортам: action и reducer для одной фичи оказываются в разных папках.

Паттерн “по фичам” (рекомендуется)

Лучше группировать код по функциональным областям (фичам). Тогда action, reducer, селекторы и тесты для одной сущности находятся рядом:

store/

features/

  todos/

  etc/

sagas/

middleware/

root-reducer.js

root-action.js

index.js

Импорт упрощается:

import { todosActions, todosReducer } from 'store/features/todos'

Можно держать Redux в отдельной папке (/store) или интегрировать в src/. Если компоненты привязаны к фичам, удобно хранить компоненты и reducers рядом.

Файлы типов в TypeScript

В каждом модуле фичи можно добавить types.ts для описания интерфейсов состояния и типов actions. Это упрощает рефакторинг и гарантирует согласованность.

Установка и базовая настройка

Установка через npm:

npm install redux react-redux

Для разработки полезен пакет redux-devtools (или Redux DevTools Extension):

npm install --save-dev redux-devtools

Создаём store. Сохраните файл как /store/index.js:

import { createStore } from 'redux'

import rootReducer from ‘./root-reducer’

const store = createStore(rootReducer)

export default store;


Это минимальная конфигурация — позже вы подключите middleware, enhancers, роутер и т. п.

## Пример фичи: список задач (todos)

Для примера создадим простую фичу todo. Начнём с типов действий: создайте /features/todos/types.js

export const ADD = ‘ADD_TODO’


export const DELETE = 'DELETE_TODO'

export const EDIT = 'EDIT_TODO'

Затем actions: /store/features/todos/actions.js

import * as types from './types.js'

export const addTodo = text => ({ type: types.ADD, text })

export const deleteTodo = id => ({ type: types.DELETE, id })

export const editTodo = (id, text) => ({ type: types.EDIT, id, text })


Reducer: /store/features/todos/reducer.js

import * as types from ‘./types.js’


const initialState = [

{

text: 'Hello World',

id: 0

}

]

export default function todos(state = initialState, action) {

switch (action.type) {

case types.ADD:

return [

...state,

{

id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,

text: action.text

}

]

case types.DELETE:

return state.filter(todo =>

todo.id !== action.id

)

case types.EDIT:

return state.map(todo =>

todo.id === action.id ? { ...todo, text: action.text } : todo

)

default:

return state

}

}

Пояснения к reducer:

  • ADD создаёт новый объект задачи и добавляет его в массив, вычисляя уникальный id.
  • DELETE фильтрует задачи по id.
  • EDIT возвращает новый массив, где изменённый элемент клонируется с обновлённым text.

Изначальное состояние (initialState) представляет собой только часть глобального состояния — branch state.todos.

Комбинирование редьюсеров

В /store/root-reducer.js импортируйте reducers и используйте combineReducers:

import { combineReducers } from 'redux';

import todosReducer from ‘./features/todos/reducer’;

const rootReducer = combineReducers({

todos: todosReducer

})

export default rootReducer


Это гарантирует, что каждая фича занимает свою ветвь в глобальном state.

## Интеграция с React

Обёрните приложение в Provider, чтобы пробросить store во все компоненты:

import React from ‘react’;


import ReactDOM from 'react-dom';

// Redux Setup

import { Provider } from 'react-redux';

import store, { history } from './store';

ReactDOM.render(







, document.getElementById('root'));

Для взаимодействия с Redux в функциональных компонентах удобно использовать хуки: useDispatch и useSelector.

Пример контейнера добавления задач:

import React, { useState } from 'react';

import ‘./Home.css’;

import { TodoList } from ‘components’

import { todosActions } from ‘store/features/todos’

import { useDispatch } from ‘react-redux’

function Home() {

const dispatch = useDispatch();

const [text, setText] = useState(“”);

function handleClick() {

dispatch(todosActions.addTodo(text));

setText(“”);

}

function handleChange(e: React.ChangeEvent) {

setText(e.target.value);

}

return (

);

}

export default Home;


Пример списка задач с useSelector:

import React from ‘react’;


import { useSelector } from 'store'

import { Container, List, ListItem, Title } from './styles'

function TodoList() {

const posts = useSelector(state => state.todos)

return (





{posts.map(({ id, title }) => (



{title} : {id}



))}





);

}

export default TodoList;

Замечание: в реальном коде стоит использовать ключи элементов, уникальные по id, а не по title.

Middleware, Sagas, Thunks и побочные эффекты

Redux по умолчанию синхронен. Для асинхронных задач используют middleware:

  • redux-thunk — простой и популярный для небольших случаев (функции вместо actions);
  • redux-saga — побочные эффекты выражаются в виде генераторов, удобен для сложных потоков и отмены задач;
  • redux-observable — использует RxJS и хорошо подходит для реактивных потоков.

Выбор зависит от требований команды и сложности: если нужно много управления параллелизмом и отменой — saga; если несколько запросов — thunk.

Оптимизация производительности

Проблемы перерисовок часто связаны с неправильными селекторами или частыми изменениями ссылок на объекты.

Рекомендации:

  • Нормализуйте состояние (как в базе данных), чтобы избежать глубоких копий и переприсваиваний.
  • Используйте мемоизированные селекторы — библиотека reselect.
  • Разбивайте state на независимые ветви и комбинируйте reducers.
  • Избегайте избыточных dispatch в render-пайплайне.

Пример простой мемоизации с reselect:

import { createSelector } from 'reselect'

const getTodos = state => state.todos

export const getVisibleTodos = createSelector([getTodos, (state, filter) => filter], (todos, filter) => {
  // вычисления, которые выполняются только при изменении зависимостей
  return todos.filter(todo => /* фильтрация по filter */ true)
})

Шаблоны проектирования и лучшие практики

  • Duck-паттерн: храните actions, types и reducer в одном модуле для фичи.
  • Action creators: используйте функции, которые формируют объект действия; это упрощает тестирование.
  • Минимизируйте логику в компонентах — выносите её в селекторы, thunks/sagas или utils.
  • Тестируйте reducers и селекторы отдельно — они чистые функции и легко тестируются.

Тестирование

Тестируйте три уровня:

  1. Unit tests для reducers и селекторов (чистые функции).
  2. Integration tests для thunks/sagas (можно мокать API).
  3. E2E тесты для пользовательских потоков.

Пример теста reducer (псевдокод):

const state = []
const action = { type: types.ADD, text: 'test' }
const nextState = todos(state, action)
expect(nextState.length).toBe(1)
expect(nextState[0].text).toBe('test')

Приём добавления новой фичи в Redux — чеклист

Для разработчика:

  • Создал директорию /store/features/
  • Добавил types.js, actions.js, reducer.js
  • Добавил селекторы в selectors.js
  • Написал unit-тесты для reducer и селекторов
  • Добавил интеграционные тесты для асинхронной логики (если есть)
  • Зарегистрировал reducer в root-reducer
  • Протестировал UI: добавление/удаление/редактирование

Для ревьюера/архитектора:

  • Логическая изоляция фичи (минимальные зависимости)
  • Понятные названия типов действий
  • Отсутствие побочных эффектов в reducer
  • Мемоизация селекторов при необходимости
  • Документация и комментарии к нестандартной логике

Альтернативы Redux и когда не стоит его использовать

Когда Redux может быть избыточен:

  • Небольшие приложения с локальным состоянием — Context API или локальные useState/useReducer достаточно.
  • UI с минимальным обменом данных между компонентами.

Популярные альтернативы:

  • Context API + useReducer — простая замена для небольших случаев.
  • MobX — реактивная модель, проще в настройке для CRUD-приложений, но менее предсказуемая.
  • Recoil — библиотека от Facebook с атомарным состоянием.
  • Zustand — лёгкий, минималистичный менеджер состояния.

Когда выбирать Redux:

  • Сложная логика синхронизации состояния между множеством компонентов.
  • Требуется мощная отладка (DevTools, time-travel).
  • Нужна строгая предсказуемость и тестируемость редьюсеров.

Примеры типичных проблем и как их решать

  1. Перерисовки компонентов при неизменности данных

    • Проверьте, не создаются ли новые объекты в селекторах.
    • Используйте reselect или useMemo.
  2. Дублирование типов действий

    • Организуйте namespace для типов, например todos/ADD.
  3. Смешивание побочных эффектов и редьюсеров

    • Перенесите асинхронную логику в thunks/sagas; редьюсеры должны быть чистыми.
  4. Сложности с типизацией в TypeScript

    • Используйте Redux Toolkit или typesafe-actions; экспортируйте типы экшенов и состояния.

Примеры конфигураций middleware

Подключение redux-thunk:

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './root-reducer'

const store = createStore(rootReducer, applyMiddleware(thunk))

export default store

Подключение Redux DevTools (безопасный pattern):

import { createStore, applyMiddleware, compose } from 'redux'

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose

const store = createStore(rootReducer, composeEnhancers(applyMiddleware(...middleware)))

Пример перехода с Context API на Redux — простой план миграции

  1. Идентифицируйте глобальные данные и места использования.
  2. Создайте структуру features и временно пробросьте store через Provider.
  3. Постепенно перепишите потребителей: замените useContext -> useSelector, а колбэки на dispatch.
  4. Удалите устаревший Context после полной миграции.

Полезные паттерны разработки

  • Normalization: храните сущности как словари по id, а не массивы больших объектов.
  • Selectors: инкапсулируют логику выборки из state.
  • Action creators как единственная точка формирования payload (удобно для логирования).
  • Immutable updates: используйте spread, map, filter или библиотеки типа Immer для безопасных мутаций.

Маленькая шпаргалка команд и зависимостей

  • Установка Redux и React-Redux: npm install redux react-redux
  • Devtools: установить расширение браузера или пакет redux-devtools
  • Middleware: redux-thunk, redux-saga, redux-logger
  • Утилиты: reselect, normalizr, immer

Decision flowchart для выбора менеджера состояния

flowchart TD
  A[Небольшое приложение?] -->|Да| B[Используйте useState/useReducer или Context API]
  A -->|Нет| C[Нужно централизованное управление данных?]
  C -->|Нет| B
  C -->|Да| D[Есть сложные побочные эффекты?]
  D -->|Да| E[Рассмотрите Redux + Saga]
  D -->|Нет| F[Redux + Thunk или Zustand]
  E --> G[Требуется time-travel/DevTools?]
  G -->|Да| H[Redux 'DevTools']
  G -->|Нет| F

Примеры ролей: что должен делать разработчик, ревьюер и архитектор

Разработчик:

  • Реализует фичу в папке feature
  • Пишет тесты для reducer
  • Проводит ручное тестирование UI

Ревьюер:

  • Проверяет чистоту reducer
  • Смотрит на названия action types
  • Проверяет мемоизацию селекторов

Архитектор:

  • Утверждает паттерн структурирования
  • Контролирует подключение middleware
  • Обеспечивает соответствие требованиям безопасности и масштабируемости

Критерии приёмки

  • Все actions имеют явные типы и payload
  • Reducer не выполняет побочных эффектов
  • Нет лишних перерисовок компонентов
  • Тесты для reducer и селекторов покрывают ключевые сценарии

Примеры приёмочных тестов

  • Добавление задачи увеличивает длину массива
  • Удаление задачи с несуществующим id не ломает состояние
  • Редактирование задачи изменяет только поле text у нужного id

Частые ошибки и как их избежать

  • Хранение больших UI-станов в Redux (формы, локальные поля) — держите их локально.
  • Мутирование состояния в reducer — используйте чистые операции и не изменяйте входной state.
  • Использование title как key в списках — используйте уникальный id.

Советы по отладке

  • Подключите Redux DevTools и отслеживайте последовательность actions.
  • Логируйте payload в action creators временно для отладки.
  • Используйте hot-reload редьюсеров в development для быстрой итерации.

Security и приватность

Redux хранит данные в памяти клиента. Не храните в state секреты: токены доступа, пароли, PII — держите их в защищённом хранилище и минимально передавайте в client-side.

Краткая методология внедрения Redux в проект

  1. Пилотная фича: выберите одну среднюю по сложности фичу и реализуйте на Redux.
  2. Оценка: проверьте сложность, скорость разработки, удобство отладки.
  3. Итерация: доработайте структуру модулей и селекторов.
  4. Масштабирование: постепенно переносите другие фичи.

Глоссарий 1 строкой

  • Store — контейнер состояния.
  • Action — дескриптор события { type, payload }.
  • Reducer — чистая функция, возвращающая новый state.
  • Middleware — прослойка для перехвата/обработки dispatch.
  • Selector — функция выбора части состояния.

Когда Redux не подходит

  • Простая форма с локальным состоянием.
  • Приложение с почти нулевым взаимодействием между компонентами.

Ресурсы для изучения

  • Официальная документация Redux
  • Redux Toolkit для упрощённой конфигурации
  • Ресурсы по redux-saga и redux-thunk

FAQ

Какой минимальный набор файлов для простой фичи?

Минимально: types.js, actions.js, reducer.js и подключение reducer в root-reducer.

Нужно ли всегда использовать combineReducers?

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

Как выбрать между redux-thunk и redux-saga?

Если нужны простые асинхронные вызовы — thunk. Если сложная координация асинхронных потоков и отмена задач — saga.


Краткое резюме

Redux даёт предсказуемую модель управления состоянием, которая особенно полезна в больших приложениях с множеством взаимодействий. Для небольших проектов его можно заменить более лёгкими инструментами. При внедрении важно придерживаться чистых редьюсеров, нормализации состояния и мемоизации селекторов.

Важно: начните с малого — пилотная фича позволит оценить ценность Redux для вашего проекта.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Троян Herodotus: как он работает и как защититься
Кибербезопасность

Троян Herodotus: как он работает и как защититься

Включить новое меню «Пуск» в Windows 11
Windows 11

Включить новое меню «Пуск» в Windows 11

Панель полей PivotTable в Excel — руководство
Excel

Панель полей PivotTable в Excel — руководство

Включить новый Пуск в Windows 11 — инструкция
Windows

Включить новый Пуск в Windows 11 — инструкция

Как убрать дубликаты Диспетчера задач Windows 11
Windows

Как убрать дубликаты Диспетчера задач Windows 11

Как просмотреть историю просмотров Reels в Instagram
Социальные сети

Как просмотреть историю просмотров Reels в Instagram