useReducer

useReducer is an alternative to useState for managing more complex state. You describe how state changes in one pure reducer function — (state, action) => newState — and components trigger changes by dispatching actions . All your update logic lives in one place, which makes complex state predictable and easy to test.

Learn useReducer in our free React course — a beginner-friendly interactive lesson with runnable examples, a practice exercise and a quick recall.

Part of the free React course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

1️⃣ A Reducer Is (state, action) => newState

The reducer is a plain function that takes the current state and an action (an object describing what happened, usually with a type ) and returns the next state. A switch on action.type keeps it tidy.

2️⃣ dispatch — Sending Actions

useReducer gives you [state, dispatch] . You don't change state directly; you call dispatch(action) , React runs the reducer with the current state, stores the result, and re-renders. Actions often carry data: {' '} .

Your turn — complete a toggle reducer for a light switch:

3️⃣ Reducers Must Be Pure & Immutable

A reducer must not mutate the old state or cause side effects — given the same inputs it always returns the same output. To change one field, spread the old state into a new object and override the field: {' '} . This produces a fresh reference, which is how React detects the change.

These lines form a complete counter reducer. Put them in the right order:

📋 Quick Reference

(state, action) => newState — a pure function returning the next state.

2. A reducer does state.count++; return state; . What's wrong?

It mutates the old state and returns the same reference, so React may not re-render. Return {' '} .

3. You dispatch {' '} and the reducer has a default: return state . What happens?

Nothing changes — the default returns the existing state unchanged.

Write a reducer handling addItem, removeItem, and empty, then dispatch a sequence and log the final count. Run it and check your output.

Practice quiz

What is the signature of a reducer?

  • (action) => state
  • (state) => action
  • (state, action) => newState
  • (newState, oldState) => action

Answer: (state, action) => newState. A reducer is a pure function (state, action) => newState that computes the next state from the current state and an action.

What does dispatch(action) do?

  • Runs the reducer with the current state and action, stores the result, and re-renders
  • Mutates state directly
  • Creates a new reducer
  • Returns the previous state

Answer: Runs the reducer with the current state and action, stores the result, and re-renders. dispatch sends an action to the reducer; React runs reducer(currentState, action), stores the returned value as new state, and re-renders.

What does useReducer return?

useReducer(reducer, initialState) returns a [state, dispatch] pair. You call dispatch(action) to trigger updates.

Why must a reducer be pure and avoid mutating the old state?

  • To make it run faster
  • React compares state by reference; mutating keeps the same reference and updates can be missed
  • Because mutation is a syntax error
  • So the reducer can be async

Answer: React compares state by reference; mutating keeps the same reference and updates can be missed. React compares previous and next state by reference. Mutating the old object keeps the same reference, so React may not detect the change. Return a new object.

How do you correctly update one field of state in a reducer?

  • return { ...state, name: action.name };
  • state.name = action.name; return state;
  • return action.name;
  • state = { name: action.name };

Answer: return { ...state, name: action.name };. Spread the old state into a new object and override the field: { ...state, name: action.name }. This produces a fresh reference.

What is the typical shape of an action?

  • A string only
  • A function
  • An object, usually { type, payload }
  • An array of states

Answer: An object, usually { type, payload }. Actions are usually objects with a type describing what happened, often plus a payload of data, e.g. { type: 'add', amount: 10 }.

You dispatch { type: 'nope' } and the reducer has default: return state. What happens?

  • React throws an error
  • Nothing changes — the default returns the existing state
  • State becomes undefined
  • The reducer runs twice

Answer: Nothing changes — the default returns the existing state. An unknown action type falls through to the default case, which returns the existing state unchanged, so nothing updates.

When is useReducer a better choice than useState?

  • When you have a single boolean
  • When you never update state
  • When you want to avoid a render
  • When state is complex with several related fields, or many handlers update it in different ways

Answer: When state is complex with several related fields, or many handlers update it in different ways. useReducer centralizes complex update logic in one place — ideal for complex state, or when many handlers update the same state differently.

A reducer does state.count++; return state;. What is wrong?

  • Nothing, it is correct
  • It mutates the old state and returns the same reference, so React may not re-render
  • count cannot be incremented
  • It needs a payload

Answer: It mutates the old state and returns the same reference, so React may not re-render. It mutates state and returns the same reference. Return a new object: { ...state, count: state.count + 1 }.

Where should side effects like data fetching go?

  • Inside the reducer
  • In the default case
  • In event handlers or effects, not in the reducer
  • In the action object

Answer: In event handlers or effects, not in the reducer. Reducers must be pure. Do fetches, logging, and other side effects in event handlers or effects, never inside the reducer.