Что такое Redux и как он работает в React?
Что такое Redux
Redux — библиотека для управления глобальным состоянием приложения. Она не привязана к React, но чаще всего используется именно с ним через пакет react-redux.
Redux строится на трёх фундаментальных принципах:
- Единственный источник истины — всё состояние приложения хранится в одном объекте (
store). - Состояние только для чтения — изменить состояние можно только через отправку (
dispatch) действия (action). - Изменения через чистые функции — редьюсеры (
reducer) описывают, как действие изменяет состояние, и не имеют побочных эффектов.
Ключевые понятия
Store
Центральное хранилище всего состояния. Создаётся один раз через configureStore (Redux Toolkit) или createStore.
Action
Простой объект с обязательным полем type, описывающий намерение изменить состояние.
Reducer
Чистая функция (state, action) => newState. Получает текущее состояние и действие, возвращает новое состояние. Никогда не мутирует исходный объект.
Dispatch
Метод store для отправки действий. После вызова dispatch(action) Redux передаёт действие в редьюсер и обновляет состояние.
Selector
Функция для извлечения нужного куска состояния из store. Хук useSelector подписывает компонент на изменения.
Поток данных
UI → dispatch(action) → reducer(state, action) → новый state → UI
Поток строго однонаправленный: это делает поведение приложения предсказуемым и легко отлаживаемым.
Пример с Redux Toolkit
import { configureStore, createSlice, PayloadAction } from '@reduxjs/toolkit'
// Описываем срез состояния
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
// Внутри RTK можно «мутировать» — Immer делает это безопасно
increment(state) {
state.value += 1
},
addByAmount(state, action: PayloadAction<number>) {
state.value += action.payload
},
},
})
export const { increment, addByAmount } = counterSlice.actions
export const store = configureStore({
reducer: { counter: counterSlice.reducer },
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
Подключение к React
import { Provider } from 'react-redux'
import { store } from './store'
// Оборачиваем приложение в Provider
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
)
}
import { useSelector, useDispatch } from 'react-redux'
import { increment, addByAmount } from './counterSlice'
import type { RootState, AppDispatch } from './store'
function Counter() {
// Читаем состояние — компонент перерендерится при его изменении
const count = useSelector((state: RootState) => state.counter.value)
const dispatch = useDispatch<AppDispatch>()
return (
<div>
<span>{count}</span>
<button onClick={() => dispatch(increment())}>+1</button>
<button onClick={() => dispatch(addByAmount(5))}>+5</button>
</div>
)
}
Когда использовать Redux
Redux оправдан, когда состояние действительно глобальное (авторизация, корзина, фильтры), когда несколько несвязанных компонентов должны реагировать на одни данные, или когда важна воспроизводимость и возможность time-travel дебаггинга через Redux DevTools.
Что хочет услышать интервьюер
Понимание трёх принципов Redux: единый store, иммутабельность, чистые редьюсеры
Знание однонаправленного потока данных: action → reducer → store → UI
Умение объяснить роли каждой сущности (store, action, reducer, dispatch, selector)
Знакомство с Redux Toolkit как современным стандартом — configureStore, createSlice
Понимание, когда Redux нужен, а когда достаточно Context или useState
Пример: Слайс и store (Redux Toolkit)
import { configureStore, createSlice, PayloadAction } from '@reduxjs/toolkit'
interface CounterState {
value: number
}
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 } as CounterState,
reducers: {
increment(state) {
state.value += 1 // Immer разрешает «мутацию» внутри createSlice
},
addByAmount(state, action: PayloadAction<number>) {
state.value += action.payload
},
reset(state) {
state.value = 0
},
},
})
export const { increment, addByAmount, reset } = counterSlice.actions
export const store = configureStore({
reducer: {
counter: counterSlice.reducer,
},
})
// Типы для использования в хуках
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
Пример: Подключение и использование в компонентах
import React from 'react'
import { Provider, useSelector, useDispatch } from 'react-redux'
import { store, increment, addByAmount, reset } from './store'
import type { RootState, AppDispatch } from './store'
// Типизированные хуки — лучше вынести в отдельный файл hooks.ts
const useAppSelector = useSelector.withTypes<RootState>()
const useAppDispatch = useDispatch.withTypes<AppDispatch>()
function Counter() {
const count = useAppSelector((state) => state.counter.value)
const dispatch = useAppDispatch()
return (
<div>
<p>Счётчик: {count}</p>
<button onClick={() => dispatch(increment())}>+1</button>
<button onClick={() => dispatch(addByAmount(10))}>+10</button>
<button onClick={() => dispatch(reset())}>Сбросить</button>
</div>
)
}
export function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
)
}
Типичные ошибки
Мутация состояния напрямую в редьюсере вместо возврата нового объекта
Использование Redux для локального состояния форм или UI-флагов, где достаточно useState
Незнание Redux Toolkit — кандидат описывает только устаревший шаблон с `createStore` и `combineReducers`
Путаница между action creator и action — кандидат не разграничивает функцию и объект
Отсутствие типизации store и useSelector в TypeScript-проекте


