How to Debug useReducer Hook in React

The useReducer hook is a powerful tool for managing complex state in React, but debugging it can be challenging when state transitions do not behave as expected. This article provides a straightforward guide on how to effectively debug useReducer using console logging, custom logger wrappers, React DevTools, and browser breakpoints to keep your state management predictable and bug-free.

1. Log Inside the Reducer Function

The simplest way to track state transitions is to add console.log statements directly inside your reducer function. Because the reducer is a pure function that receives the current state and the dispatched action, logging these values allows you to see exactly what triggered a state change and what the resulting state will be.

function reducer(state, action) {
  console.log('Previous State:', state);
  console.log('Dispatched Action:', action);

  switch (action.type) {
    case 'increment':
      const nextState = { count: state.count + 1 };
      console.log('Next State:', nextState);
      return nextState;
    default:
      return state;
  }
}

2. Create a Custom Logger Hook

If you use useReducer frequently, wrapping it in a custom hook that automatically logs state changes can save time. This mimics the behavior of popular Redux logging middleware.

import { useReducer, useCallback, useRef } from 'react';

function useLoggedReducer(reducer, initialState) {
  const [state, dispatch] = useReducer(reducer, initialState);

  const dispatchWithLog = useCallback((action) => {
    console.group(`Action: ${action.type}`);
    console.log('%c Prev State:', 'color: #9E9E9E; font-weight: bold;', state);
    console.log('%c Action:', 'color: #03A9F4; font-weight: bold;', action);
    
    dispatch(action);
  }, [state]);

  // To log the next state after render, use a ref or useEffect
  return [state, dispatchWithLog];
}

By replacing useReducer with useLoggedReducer during development, you get clean, grouped console logs for every state transition.

3. Inspect State with React DevTools

React DevTools is an essential browser extension for debugging hooks.

  1. Open your browser’s developer tools and navigate to the Components tab.
  2. Select the component utilizing the useReducer hook.
  3. In the right-hand panel, look under the Hooks section.
  4. You will see a Reducer hook entry. Expand it to inspect the current state value in real-time as actions are dispatched.

4. Use the debugger Statement

When console logs are not enough, you can pause JavaScript execution using the debugger statement inside your reducer. This allows you to inspect the call stack and see exactly which component dispatched the action.

function reducer(state, action) {
  switch (action.type) {
    case 'complex_action':
      debugger; // Browser execution will pause here if DevTools is open
      return { ...state, data: action.payload };
    default:
      return state;
  }
}

When the execution pauses, use your browser’s sources panel to step through the code line by line and examine the local variables.