← Назад к вопросам

Что делает reducer в Redux?

1.0 Junior🔥 221 комментариев
#React#State Management

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Reducer в Redux

Reducer — это чистая функция (pure function), которая принимает два аргумента: текущее состояние (state) и действие (action), а затем возвращает новое состояние. Это сердце Redux архитектуры.

Определение

// Сигнатура reducer:
(previousState, action) => newState

// Пример:
function counterReducer(state = 0, action) {
  switch (action.type) {
    case "INCREMENT":
      return state + 1;
    case "DECREMENT":
      return state - 1;
    default:
      return state;
  }
}

Основные принципы Reducer

1. Должен быть чистой функцией (Pure Function)

Это значит:

  • Одинаковый вход → одинаковый выход
  • Нет побочных эффектов
  • Не мутирует входные аргументы
  • Не выполняет асинхронные операции
// ❌ НЕПРАВИЛЬНО — reducer мутирует state
function badReducer(state, action) {
  if (action.type === "SET_NAME") {
    state.user.name = action.payload; // мутация!
    return state;
  }
  return state;
}

// ✅ ПРАВИЛЬНО — создаём новый объект
function goodReducer(state, action) {
  if (action.type === "SET_NAME") {
    return {
      ...state,
      user: {
        ...state.user,
        name: action.payload
      }
    };
  }
  return state;
}

2. Должен иметь начальное состояние

// Старый стиль (ES5)
function todoReducer(state, action) {
  if (state === undefined) {
    state = [];
  }
  // ...
}

// Современный стиль (с параметром по умолчанию)
function todoReducer(state = [], action) {
  switch (action.type) {
    case "ADD_TODO":
      return [...state, action.payload];
    default:
      return state;
  }
}

Полный пример: Todo Reducer

const initialState = {
  todos: [],
  filter: "all" // all, active, completed
};

function todoReducer(state = initialState, action) {
  switch (action.type) {
    // Добавление todo
    case "ADD_TODO":
      return {
        ...state,
        todos: [
          ...state.todos,
          {
            id: Date.now(),
            text: action.payload,
            completed: false
          }
        ]
      };
    
    // Удаление todo
    case "DELETE_TODO":
      return {
        ...state,
        todos: state.todos.filter(todo => todo.id !== action.payload)
      };
    
    // Переключение статуса todo
    case "TOGGLE_TODO":
      return {
        ...state,
        todos: state.todos.map(todo =>
          todo.id === action.payload
            ? {...todo, completed: !todo.completed}
            : todo
        )
      };
    
    // Установка фильтра
    case "SET_FILTER":
      return {
        ...state,
        filter: action.payload
      };
    
    default:
      return state;
  }
}

Как Reducer работает в Redux

import { createStore } from redux;

// Создаём store с reducer
const store = createStore(todoReducer);

// Отправляем действие
store.dispatch({
  type: "ADD_TODO",
  payload: "Learn Redux"
});

// Reducer обрабатывает действие и обновляет состояние
// Старое состояние: { todos: [], filter: "all" }
// Действие: { type: "ADD_TODO", payload: "Learn Redux" }
// Новое состояние: { 
//   todos: [{id: 1234, text: "Learn Redux", completed: false}], 
//   filter: "all" 
// }

// Получаем новое состояние
const state = store.getState();
console.log(state);

Combine Reducers (несколько reducers)

const userReducer = (state = {name: "", role: "user"}, action) => {
  switch (action.type) {
    case "SET_USER":
      return action.payload;
    default:
      return state;
  }
};

const themeReducer = (state = "light", action) => {
  switch (action.type) {
    case "TOGGLE_THEME":
      return state === "light" ? "dark" : "light";
    default:
      return state;
  }
};

// Объединяем reducers
const rootReducer = combineReducers({
  user: userReducer,
  theme: themeReducer
});

const store = createStore(rootReducer);

// Структура состояния:
// {
//   user: {name: "", role: "user"},
//   theme: "light"
// }

Reducer с payload (расширенный пример)

const initialState = {
  items: [],
  loading: false,
  error: null
};

function itemsReducer(state = initialState, action) {
  switch (action.type) {
    case "FETCH_START":
      return {
        ...state,
        loading: true,
        error: null
      };
    
    case "FETCH_SUCCESS":
      return {
        ...state,
        items: action.payload,
        loading: false
      };
    
    case "FETCH_ERROR":
      return {
        ...state,
        loading: false,
        error: action.payload
      };
    
    case "ADD_ITEM":
      return {
        ...state,
        items: [...state.items, action.payload]
      };
    
    default:
      return state;
  }
}

Действия (Actions) vs Reducer

// Action — просто объект с информацией что произошло
const action = {
  type: "ADD_TODO",
  payload: "Buy milk" // данные
};

// Reducer — функция, которая определяет КАК изменить состояние
function reducer(state, action) {
  if (action.type === "ADD_TODO") {
    return {
      ...state,
      todos: [...state.todos, action.payload]
    };
  }
  return state;
}

Типичная ошибка: прямое изменение состояния

// ❌ НЕПРАВИЛЬНО
function badReducer(state = {count: 0}, action) {
  if (action.type === "INCREMENT") {
    state.count++; // МУТАЦИЯ!
    return state;
  }
  return state;
}

// ✅ ПРАВИЛЬНО
function goodReducer(state = {count: 0}, action) {
  if (action.type === "INCREMENT") {
    return {
      ...state,
      count: state.count + 1
    };
  }
  return state;
}

// Почему это важно?
// Redux использует === для проверки изменений
// Если вернуть тот же объект, Redux не обнаружит изменение!
const oldState = {count: 0};
const newState = badReducer(oldState, {type: "INCREMENT"});

console.log(oldState === newState); // true (один и тот же объект!)
console.log(oldState.count); // 1 (изменилось!)

Reducer с сложной логикой

function cartReducer(state = {items: [], total: 0}, action) {
  switch (action.type) {
    case "ADD_TO_CART":
      const newItems = [...state.items, action.payload];
      const newTotal = newItems.reduce((sum, item) => sum + item.price, 0);
      return {
        items: newItems,
        total: newTotal
      };
    
    case "REMOVE_FROM_CART":
      const filteredItems = state.items.filter(
        item => item.id !== action.payload
      );
      const updatedTotal = filteredItems.reduce(
        (sum, item) => sum + item.price,
        0
      );
      return {
        items: filteredItems,
        total: updatedTotal
      };
    
    default:
      return state;
  }
}

Использование в React компоненте (Redux Hooks)

import { useSelector, useDispatch } from react-redux;

function TodoApp() {
  const todos = useSelector(state => state.todos);
  const dispatch = useDispatch();
  
  const addTodo = (text) => {
    dispatch({
      type: "ADD_TODO",
      payload: text
    });
  };
  
  return (
    <div>
      {todos.map(todo => (
        <div key={todo.id}>{todo.text}</div>
      ))}
      <button onClick={() => addTodo("New task")}>
        Add Todo
      </button>
    </div>
  );
}

Резюме: Что делает Reducer?

  1. Принимает текущее состояние и действие (action)
  2. Вычисляет новое состояние на основе типа действия
  3. Возвращает новое состояние (никогда не мутирует старое)
  4. Является чистой функцией — предсказуемой и без побочных эффектов
  5. Обновляет Redux store, который уведомляет все подписанные компоненты

Аналогия: Если dispatch — это отправка письма (action), то reducer — это почтальон, который на основе содержания письма решает, как изменить содержимое шкафа (state).

Что делает reducer в Redux? | PrepBro