DEV Community

Cristian Sifuentes
Cristian Sifuentes

Posted on

Mastering `useReducer` in React: A Step-by-Step Guide for Real-World State Management

Mastering  raw `useReducer` endraw  in React: A Step-by-Step Guide for Real-World State Management

Mastering useReducer in React: A Step-by-Step Guide for Real-World State Management

In modern React development, managing state predictably is key to building maintainable, scalable applications. While useState handles simple state needs, useReducer shines when your component’s state logic becomes more complex or when the next state depends on the previous one.

In this article, we'll break down the useReducer hook in depth and show how it enables clear, scalable, and testable state transitions. We’ll also build a mathematical counter using reducer patterns, covering advanced JavaScript concepts like the spread operator, custom actions, and strict typing with TypeScript.


What Is useReducer?

useReducer is a React hook that provides an alternative to useState. It’s commonly used when:

  • State logic is complex.
  • The next state depends on the previous one.
  • You need to centralize state transitions.

It works by dispatching actions to a reducer function, which then returns the new state.

const [state, dispatch] = useReducer(reducerFunction, initialState);
Enter fullscreen mode Exit fullscreen mode

Reducer Function Explained

A reducer is a pure function. Given the current state and an action, it returns a new state. It does not mutate the existing state directly.

type ActionType = {
  type: 'increment' | 'decrement' | 'custom';
  payload?: number;
};

const reducerCounter = (state: typeof initialState, action: ActionType) => {
  switch (action.type) {
    case 'increment':
      return { ...state, counter: state.counter + 1 };
    case 'decrement':
      return { ...state, counter: state.counter - 1 };
    case 'custom':
      return { ...state, counter: state.counter + (action.payload || 0) };
    default:
      return state;
  }
};
Enter fullscreen mode Exit fullscreen mode

What’s the Spread Operator?

The spread operator (...) allows cloning and extending an object. Here it ensures immutability by copying the old state while updating specific properties.


Real Example: Counter Component Using useReducer

import { useReducer } from 'react';

const initialState = { counter: 10 };

export const CounterRed = () => {
  const [state, dispatch] = useReducer(reducerCounter, initialState);

  return (
    <div className="math-card">
      <h3>🧮 Reducer Counter</h3>
      <p>Counter: {state.counter}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+1</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
      <button onClick={() => dispatch({ type: 'custom', payload: 10 })}>+10</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Why Prefer useReducer Over useState?

Feature useState useReducer
Simple state ✅ Easy 🚫 Overkill
Complex state logic 🚫 Hard to manage ✅ Centralized
Depends on previous state 🔁 Risky ✅ Predictable
Testability 😐 Limited ✅ Clean and testable

Bonus Tips for Mastery

  • Combine useReducer with useContext for global state management.
  • Use useCallback to avoid unnecessary renders of dispatch-bound functions.
  • Structure your actions and types clearly for maintainability.

Final Thoughts

React’s useReducer provides a powerful toolset for managing state declaratively. It’s especially useful when dealing with branching logic or state updates that are contingent on other values.

By building a simple but extendable mathematical counter, you now have a blueprint to implement this hook in more advanced real-world scenarios—such as forms, API state, and more.

With this knowledge, you’re one step closer to mastering modern React state management with useReducer.


Tags

react, javascript, hooks, reducer, frontend, typescript, performance

Top comments (0)