> Как работают редьюсеры в Redux (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: SmartWay

Стек: JavaScript

> Пример ответа

Короткий ответ

Редьюсер в Redux - это чистая функция (state, action) => newState, которая определяет, как состояние приложения изменяется в ответ на action. Она принимает текущее состояние и объект action, и возвращает новое состояние без мутации исходного. Редьюсеры должны быть детерминированными, без side-эффектов, и обычно объединяются через combineReducers для управления разными частями store.

Подробное объяснение

Редьюсеры - основа Redux, реализующая принцип однонаправленного потока данных. Каждый редьюсер отвечает за свой срез состояния (slice). Когда dispatch отправляет action, он проходит через все редьюсеры, и каждый решает, нужно ли обновить свою часть состояния.

Ключевые правила:

  • Чистота: при одинаковых аргументах всегда возвращается одинаковый результат, без мутаций входных данных, API-вызовов или random.
  • Неизменяемость: возвращается новый объект состояния, а не модифицируется старый. Используются spread-оператор, Object.assign или Immer.
  • Дефолтный кейс: если action не обработан, возвращается текущее состояние как есть.

Типичная структура редьюсера - switch по action.type, хотя можно использовать if/else или lookup-таблицы. combineReducers генерирует корневой редьюсер, который делегирует обработку action дочерним редьюсерам по ключам.

Продвинутые паттерны:

  • Нормализация состояния для вложенных данных.
  • Использование createReducer из Redux Toolkit с builder callback, который автоматически защищает от мутаций через Immer.
  • Разделение редьюсеров по доменам (users, posts, ui) для масштабируемости.

На практике

В реальных проектах редьюсеры редко пишутся вручную - используется Redux Toolkit с createSlice. Это снижает boilerplate и исключает ошибки с мутациями. Важно следить за производительностью: избегать глубоких копий больших объектов, использовать селекторы с мемоизацией (Reselect).

Типичный workflow:

  1. Определяем начальное состояние для среза.
  2. Пишем редьюсер как набор обработчиков action.type.
  3. Комбинируем через configureStore (RTK) или combineReducers.
  4. В компонентах используем useSelector для чтения и useDispatch для отправки action.

Пример кода

JAVASCRIPT
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
items: [],
loading: false,
error: null,
};
const todosSlice = createSlice({
name: 'todos',
initialState,
reducers: {
addTodo: (state, action) => {
state.items.push({ id: Date.now(), text: action.payload, completed: false });
},
toggleTodo: (state, action) => {
const todo = state.items.find(item => item.id === action.payload);
if (todo) todo.completed = !todo.completed;
},
removeTodo: (state, action) => {
state.items = state.items.filter(item => item.id !== action.payload);
},
},
});
export const { addTodo, toggleTodo, removeTodo } = todosSlice.actions;
export default todosSlice.reducer;

Как отвечать на собеседовании

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

Структура ответа:

  1. Определение и принципы.
  2. Пример из практики с RTK.
  3. Продвинутые аспекты: композиция, производительность, тестирование.
  4. Сравнение с альтернативами (MobX, Zustand) - почему Redux выбирает редьюсеры.

Что проверяет интервьюер

  • Понимание функционального программирования: чистота, неизменяемость, детерминизм.
  • Умение проектировать состояние: как разбивать на срезы, избегать избыточности.
  • Знание экосистемы: RTK, Immer, Reselect.
  • Навыки отладки: как дебажить редьюсеры, что такое time-travel debugging.
  • Оценка производительности: когда редьюсер становится узким местом.

Типичные ошибки

  • Мутация состояния напрямую (например, state.items.push() без Immer).
  • Side-эффекты в редьюсере (API-вызовы, Math.random()).
  • Возврат undefined вместо state в дефолтном кейсе.
  • Слишком большой редьюсер, который обрабатывает все action - нарушение single responsibility.
  • Игнорирование начального состояния - приводит к ошибкам при инициализации.
  • Использование глубоких копий для всего состояния вместо точечных обновлений.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью