useReducer для сложной логики состояния

Хук useReducer — это альтернатива useState для случаев, когда логика обновления состояния становится сложной: много подсостояний, взаимозависимые значения или множество возможных действий. Он основан на концепции редюсера из Flux/Redux и хорошо подходит для управления состоянием корзины, формы с валидацией, истории навигации и других сценариев.

Основы useReducer

Функция useReducer принимает два аргумента:

  • редюсер (функцию, которая описывает, как состояние меняется в ответ на действия),
  • начальное состояние.

Она возвращает текущее состояние и функцию dispatch, которая отправляет действия в редюсер:

const [state, dispatch] = useReducer(reducer, initialState);

Структура редюсера

Редюсер — это чистая функция, принимающая текущее состояние и действие (action), и возвращающая новое состояние:

type Action = 
  | { type: 'ADD_ITEM'; payload: string }
  | { type: 'REMOVE_ITEM'; payload: number };

type State = {
  items: string[];
};

function cartReducer(state: State, action: Action): State {
  switch (action.type) {
    case 'ADD_ITEM':
      return { ...state, items: [...state.items, action.payload] };
    case 'REMOVE_ITEM':
      return {
        ...state,
        items: state.items.filter((_, index) => index !== action.payload),
      };
    default:
      return state;
  }
}

Пример использования

import { useReducer } from 'react';
import { View, Text, Button } from 'react-native';

export default function CartScreen() {
  const [state, dispatch] = useReducer(cartReducer, { items: [] });

  return (
    <View>
      <Text>Товаров: {state.items.length}</Text>
      <Button
        title="Добавить товар"
        onPress={() => dispatch({ type: 'ADD_ITEM', payload: 'Новый товар' })}
      />
      {state.items.map((item, index) => (
        <Button
          key={index}
          title={`Удалить ${item}`}
          onPress={() => dispatch({ type: 'REMOVE_ITEM', payload: index })}
        />
      ))}
    </View>
  );
}

Преимущества useReducer

  • Централизованная логика обновления состояния.
  • Легче тестировать и отлаживать: каждое действие явно описано.
  • Упрощает управление состоянием с несколькими подполями.

Когда выбирать useReducer вместо useState

Используйте useReducer, если:

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

В простых случаях (одиночные значения, независимые флаги) достаточно useState.