[React.js] - Part 13. React Hooks - useReducer Hook

Ace Lennox
19 Jan 20253 minutes to read

The useReducer Hook in React is a powerful alternative to useState, particularly when managing complex state logic. This guide explores the useReducer Hook with clear and simple examples to help you understand its syntax and use cases.


What is useReducer?

The useReducer Hook provides a way to handle state with custom logic. It is particularly useful when:

  1. Your state management involves multiple values.

  2. State updates depend on complex logic.

Unlike useState, which uses a simple updater function, useReducer allows you to centralize state updates within a reducer function, making your logic cleaner and easier to maintain.


Syntax

The useReducer Hook accepts two arguments:

  1. Reducer Function: A function that determines the state changes based on the dispatched action.

  2. Initial State: The initial value of your state, which can be a simple value or an object.

const [state, dispatch] = useReducer(reducer, initialState); 
  • state: The current state value.

  • dispatch: A function used to trigger state updates.


Example 1: Simple Counter

Let’s build a simple counter app using useReducer:

import { useReducer } from "react";
import ReactDOM from "react-dom/client";

const initialState = { count: 0 };

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, initialState);

  return (
    <div>
      <h1>Count: {state.count}</h1>
      <button onClick={() => dispatch({ type: "INCREMENT" })}>Increment</button>
      <button onClick={() => dispatch({ type: "DECREMENT" })}>Decrement</button>
      <button onClick={() => dispatch({ type: "RESET" })}>Reset</button>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Counter />);
 

This example illustrates how useReducer simplifies managing multiple state transitions (increment, decrement, and reset) in a single function.


Example 2: Managing a Shopping Cart

Now, let’s create a shopping cart application to demonstrate a more practical use case:

import { useReducer } from "react";
import ReactDOM from "react-dom/client";

const initialCart = [];

function cartReducer(state, action) {
  switch (action.type) {
    case "ADD_ITEM":
      return [...state, { id: action.id, name: action.name, quantity: 1 }];
    case "REMOVE_ITEM":
      return state.filter((item) => item.id !== action.id);
    case "INCREASE_QUANTITY":
      return state.map((item) =>
        item.id === action.id
          ? { ...item, quantity: item.quantity + 1 }
          : item
      );
    case "DECREASE_QUANTITY":
      return state.map((item) =>
        item.id === action.id && item.quantity > 1
          ? { ...item, quantity: item.quantity - 1 }
          : item
      );
    default:
      return state;
  }
}

function ShoppingCart() {
  const [cart, dispatch] = useReducer(cartReducer, initialCart);

  const addItem = (id, name) => {
    dispatch({ type: "ADD_ITEM", id, name });
  };

  return (
    <div>
      <button onClick={() => addItem(1, "Apple")}>Add Apple</button>
      <button onClick={() => addItem(2, "Banana")}>Add Banana</button>
      <h2>Shopping Cart:</h2>
      {cart.map((item) => (
        <div key={item.id}>
          <p>
            {item.name} (x{item.quantity})
            <button onClick={() => dispatch({ type: "INCREASE_QUANTITY", id: item.id })}>+</button>
            <button onClick={() => dispatch({ type: "DECREASE_QUANTITY", id: item.id })}>-</button>
            <button onClick={() => dispatch({ type: "REMOVE_ITEM", id: item.id })}>Remove</button>
          </p>
        </div>
      ))}
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<ShoppingCart />);

This example manages multiple actions (add, remove, increase, and decrease quantity) in a centralized cartReducer.


Why Use useReducer?

Here are a few reasons to use useReducer:

  1. Centralized Logic: All state transitions are handled in a single function, improving maintainability.

  2. Scalability: As your app grows, you can easily add more actions without cluttering your component logic.

  3. Predictable Updates: The reducer ensures that state updates are consistent and predictable.


Best Practices

  1. Keep Reducer Functions Pure: Avoid side effects like API calls inside the reducer. Use useEffect for side effects.

  2. Use Descriptive Action Types: Clearly name your action types to make the reducer logic self-explanatory.

  3. Group Related States: Use objects for initial state when managing multiple related values.


Conclusion

The useReducer Hook is a powerful tool for managing state logic in React, especially for applications with complex or interdependent state transitions. By mastering useReducer, you can write more maintainable and scalable React applications.

Experiment with the examples above to see how useReducer can simplify your state management workflow!


Source Code

The complete source code for this part is available on GitHub


Recent Posts

Latest Posts


logo

Explore insightful articles and tutorials on programming, web development, and mobile app creation. Dive into topics like ReactJS, Next.js, Android, iOS, and modern coding practices. Learn with practical examples and tips for building robust, responsive, and high-performing applications. Perfect for developers of all levels seeking to enhance their skills.

Social

© 2025. All rights reserved