DeployU
Interviews / Frontend Engineering / Why does this component log the old state value after calling a useState setter?

Questions

Why does this component log the old state value after calling a useState setter?

conceptual React Fundamentals Interactive Quiz Code Examples
How Different Experience Levels Approach This
Junior Engineer
Surface Level

The console.log should show the new value since we just updated it with setCount.

Senior Engineer
Production Ready

State updates in React are asynchronous. When you call setCount, React schedules an update—it doesn't happen immediately. The count variable still holds the value from the current render, so the console.log shows the old value.

To log the new value, you need to use useEffect with the state variable in its dependency array. This runs after the re-render when the new value is available.

Key insight: The functional update form setCount(c => c + 1) prevents bugs from batched updates, but doesn’t solve this logging issue—you still need useEffect to react to state changes.

What Makes the Difference?
  • Context over facts: Explains when and why, not just what
  • Real examples: Provides specific use cases from production experience
  • Trade-offs: Acknowledges pros, cons, and decision factors
Counter Component - Fix the Console Log
Your Code
import React, { useState } from 'react';

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

  const handleClick = () => {
    // Increment the state
    setCount(count + 1);

    // THE BUG: Try to log the new state value immediately
    console.log(`The new count is: ${count}`);
  };

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={handleClick}>Increment</button>
      <p>Check the console after clicking the button.</p>
    </div>
  );
}
// Validate the fix
const code = document.querySelector('[data-editor]').textContent;

// Check that useEffect is imported and used
if (!code.includes('useEffect')) {
  throw new Error('Test failed: useEffect hook not imported or used. You need useEffect to log the state after it updates.');
}

// Check that count is in the dependency array
if (!code.includes('[count]')) {
  throw new Error('Test failed: count must be in the useEffect dependency array to track state changes.');
}

// Check that console.log is inside useEffect
const useEffectMatch = code.match(/useEffect\s*\(\s*\(\s*\)\s*=>\s*{[\s\S]*?console\.log/);
if (!useEffectMatch) {
  throw new Error('Test failed: console.log should be moved inside useEffect to log the updated state.');
}

// Success!
console.log('✓ All tests passed! The component now correctly logs the updated state value.');
console.log('✓ useEffect will run after each state update, giving you access to the new count.');
Click "Run Code" to see output...
Click "Run Code" to see test results...

The Solution

import React, { useState, useEffect } from 'react';

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

  const handleClick = () => {
    setCount(count + 1);
  };

  // FIX: This effect runs *after* the re-render with the new count
  useEffect(() => {
    if (count > 0) {
      console.log(`The new count is: ${count}`);
    }
  }, [count]); // The dependency array is key!

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

Practice Question

Why shouldn't you rely on a state variable's new value immediately after calling its useState setter function in the same block of code?