CPCODELAB
React

useReducer — Complex State Logic

12 min read

When useState Isn't Enough

useReducer is a more structured alternative to useState for state that involves multiple sub-values or complex transitions. You dispatch actions to a reducer function that computes the next state.

jsx
import { useReducer } from 'react';

const initialState = { count: 0, step: 1 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + state.step };
    case 'decrement':
      return { ...state, count: state.count - state.step };
    case 'setStep':
      return { ...state, step: action.payload };
    case 'reset':
      return initialState;
    default:
      throw new Error('Unknown action: ' + action.type);
  }
}

function ConfigurableCounter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      <p>Count: {state.count} (step: {state.step})</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <input
        type="number"
        value={state.step}
        onChange={e => dispatch({ type: 'setStep', payload: Number(e.target.value) })}
      />
      <button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
    </div>
  );
}

Prefer useReducer over useState when: you have 3+ state variables that change together, next state depends on previous in non-trivial ways, or you want to unit-test your state logic in isolation.

Ready to test yourself?
Practice React— quiz & coding exercises