How to Debug useState Hook in React

Debugging the useState hook in React is essential for tracking state changes, identifying why a component is not re-rendering, and ensuring your application behaves predictably. This article covers the most effective techniques to debug React state, including using the React Developer Tools, properly logging state changes with the useEffect hook, and inspecting state transitions using functional updates.

1. Use React Developer Tools

The most efficient way to inspect state without changing your code is by using the official React Developer Tools browser extension.

  1. Install the React Developer Tools extension for Chrome, Firefox, or Edge.
  2. Open your browser’s Developer Tools (F12) and navigate to the Components tab.
  3. Select the component you want to inspect from the component tree.
  4. In the right-hand panel, look at the State section.

This panel displays the current value of all useState hooks in the selected component. You can also double-click the state values in this panel to manually edit them and see how your UI responds in real time.

2. Track State Changes with useEffect

A common mistake is trying to log the state immediately after calling the setter function:

const [count, setCount] = useState(0);

const handleClick = () => {
  setCount(count + 1);
  console.log(count); // This will log the old state, not the updated one
};

Because React state updates are scheduled and asynchronous, the console log runs before the state actually updates. To log the updated state accurately, use the useEffect hook with the state variable as a dependency:

import { useState, useEffect } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("Updated count state:", count);
  }, [count]); // This fires every time 'count' changes

  return (
    <button onClick={() => setCount(count + 1)}>Increment</button>
  );
}

3. Log Inside Functional State Updates

If you need to debug the exact value of the state right before it changes, you can use a functional update. Instead of passing a value directly to the setter, pass a function that receives the previous state:

setCount((prevCount) => {
  console.log("Previous state:", prevCount);
  const nextCount = prevCount + 1;
  console.log("Next state:", nextCount);
  return nextCount;
});

This method is highly effective for debugging race conditions or rapid state updates, as it guarantees you are inspecting the most current state value.

4. Use Breakpoints and debugger Statements

You can pause code execution directly inside your component or event handlers to inspect the scope. Insert a debugger statement right where your state is modified:

const handleUpdate = (newValue) => {
  debugger; // Browser execution will pause here if DevTools is open
  setValue(newValue);
};

When the browser pauses at the debugger line, you can hover over variables in your source code or use the Console tab to inspect the current state of your React component.