Why State Is Not a Variable

A variable declared inside a component is created fresh on every call and thrown away when the function returns, so incrementing it changes nothing you can see. One declared outside the component survives, but React 7,897 has no idea it changed, so the screen keeps the old markup until something else forces a render. State is the only value that both survives and schedules the render.

A local variable, a module variable and state, side by sideJavaScript
let outside = 0;                  // module scope: survives, but never triggers a render
function Demo() {
  let local = 0;                  // created again on every render
  const [count, setCount] = useState(0);
  const [seen, setSeen] = useState('-');
  function handleClick() {
    local++; outside++;
    setCount(count + 1);
    setSeen(String(count));       // the snapshot this render was handed
  }
  return (
    <p>local {local} | outside {outside} | state {count} | handler saw {seen}{' '}
      <button onClick={handleClick}>Click</button></p>
  );
}

Drop it into the page from Creating and Updating State and click three times: the paragraph reads local 0 | outside 3 | state 3 | handler saw 2. local is 0 because each handler incremented the variable belonging to the render on screen at the time, and the render that replaced it declared a new local = 0. outside reached 3, but is visible only because a state change forced a render that read it. The last field matters most: the handler on the third click saw 2, the value baked into that render's closure, even though it had just asked for 3.

That is the rule worth memorizing: within one render, every state value is a constant. setCount does not reach back and rewrite the count your handler closed over, so console.log(count) on the next line prints the old number. Compute the next value into a local variable if you need it in the same handler.