Part 21: useReducer Hook: Complex State Management & Redux Pattern

Part 21 Technology Updated September 12, 2026

Part 21: useReducer Hook: Complex State Management & Redux Pattern

When component state involves multiple sub-values or when the next state depends on complex conditions, useReducer provides a cleaner, more predictable alternative to useState.


1. What is a Reducer?

A reducer is a pure JavaScript function that takes the (currentState, action) and returns the newState:

function reducer(state, action) {
  switch (action.type) {
    case 'ACTION_TYPE':
      return { ...state, /* updates */ };
    default:
      return state;
  }
}

2. Complete Example: Shopping Cart State

import { useReducer } from 'react';

const initialState = {
  items: [],
  discountCode: null,
};

function cartReducer(state, action) {
  switch (action.type) {
    case 'ADD_ITEM':
      return {
        ...state,
        items: [...state.items, action.payload]
      };
    case 'REMOVE_ITEM':
      return {
        ...state,
        items: state.items.filter(item => item.id !== action.payload.id)
      };
    case 'APPLY_DISCOUNT':
      return {
        ...state,
        discountCode: action.payload.code
      };
    case 'CLEAR_CART':
      return initialState;
    default:
      throw new Error(`Unhandled action type: ${action.type}`);
  }
}

export default function ShoppingCart() {
  const [state, dispatch] = useReducer(cartReducer, initialState);

  const addItem = () => {
    dispatch({
      type: 'ADD_ITEM',
      payload: { id: Date.now(), name: 'Mechanical Keyboard', price: 99 }
    });
  };

  return (
    <div className="p-4 border rounded">
      <h2 className="text-xl font-bold">Shopping Cart ({state.items.length} items)</h2>
      <button onClick={addItem} className="btn-primary mt-2">Add Item</button>
      <button onClick={() => dispatch({ type: 'CLEAR_CART' })} className="btn-danger ml-2">Clear</button>

      <ul className="mt-4">
        {state.items.map(item => (
          <li key={item.id} className="flex justify-between py-1">
            <span>{item.name} (${item.price})</span>
            <button onClick={() => dispatch({ type: 'REMOVE_ITEM', payload: { id: item.id } })}>
              Remove
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

Key Takeaways

  • useReducer separates state logic from component rendering.
  • State transitions are explicit through dispatched action objects.
  • Ideal for complex state objects and state machines.

In Part 22, we compare useEffect with useLayoutEffect for synchronous DOM measurements!