useReducer
โš›๏ธ React 18+ ๐ŸŸข Chapter 23 of 39 ๐Ÿ“‚ Phase 10: Advanced State Management ๐Ÿ“… 2026 Edition
๐Ÿ“Œ Covered in this chapter: Reducer Definition ยท State Transitions ยท Action Objects ยท Reducer Function ยท dispatch() ยท Form/Todo Reducer ยท useState vs useReducer
When a component's state logic grows complex โ€” several related values that update together, or many different ways state can change โ€” useReducer offers a more organized alternative to multiple useState calls.
1The Reducer Pattern
๐Ÿ’ป Example 1: A Counter with useReducer
import { useReducer } from "react";

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    case "reset":
      return { count: 0 };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <>
      <p>{state.count}</p>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
    </>
  );
}
๐Ÿ” The Three Pieces:
  • reducer function: takes current state + an action, returns new state โ€” pure, predictable logic, all in one place
  • action object: describes what happened, using a type field by convention
  • dispatch: the function you call to trigger a state transition
2useState vs useReducer: When to Choose Which

For simple, independent values (a toggle, a text input), useState is simpler and more direct. Reach for useReducer when: several state values update together in response to the same action, the next state genuinely depends on the previous one in a complex way, or you have many distinct ways state can change (making a growing pile of separate useState calls and handler functions hard to follow).

3A Todo Reducer
function todoReducer(state, action) {
  switch (action.type) {
    case "add":
      return [...state, { id: Date.now(), text: action.text, done: false }];
    case "toggle":
      return state.map((todo) =>
        todo.id === action.id ? { ...todo, done: !todo.done } : todo
      );
    case "remove":
      return state.filter((todo) => todo.id !== action.id);
    default:
      return state;
  }
}

const [todos, dispatch] = useReducer(todoReducer, []);
dispatch({ type: "add", text: "Learn useReducer" });

Notice all the todo-updating logic (add, toggle, remove) lives in one place โ€” the reducer function โ€” rather than scattered across several separate handler functions in the component itself.

โš ๏ธ Mutating State Directly Inside a Reducer

The same immutability rule from Chapter 13 applies inside a reducer: always return a new state object or array, never mutate the existing state parameter directly. A reducer that mutates and returns the same reference can cause React to miss the update entirely.

๐Ÿ’ป Hands-on Interactive Practice Challenge

Build a simple like/dislike counter using useReducer with three action types: like, dislike, and reset.

React Practice Challenge โ–ถ Run in Compiler
import { useReducer } from "react";

function reducer(state, action) {
  switch (action.type) {
    case "like": return { count: state.count + 1 };
    case "dislike": return { count: state.count - 1 };
    case "reset": return { count: 0 };
    default: return state;
  }
}

function LikeButton() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <>
      <p>{state.count}</p>
      <button onClick={() => dispatch({ type: "like" })}>๐Ÿ‘</button>
      <button onClick={() => dispatch({ type: "dislike" })}>๐Ÿ‘Ž</button>
      <button onClick={() => dispatch({ type: "reset" })}>Reset</button>
    </>
  );
}
Run This Challenge in Online JS IDE โ†’
โ“ Frequently Asked Questions (FAQ)

Q Is useReducer a replacement for Redux?

Not exactly โ€” useReducer manages state within a single component (or a small tree via Context, covered next chapter), while Redux (Chapter 25) manages global state across an entire app with additional tooling like middleware and dev tools.

Q Why is the action object usually shaped with a 'type' field?

It's a strong convention (not a hard requirement) that makes reducers easy to read and predictable โ€” the reducer's switch statement branches directly on action.type, making every possible state transition explicit and easy to trace.

OC
Written by Our Compiler Technical Editorial Team
Reviewed for accuracy & tested on React 18+ ยท Last updated August 2026