Strange behavior of React hooks: delayed data update

State updates are asynchronous. The setCount function you get from useState can't magically reach out and change the value of your count constant. For one thing, it's a constant. For another, setCount doesn't have access to it. Instead, when you call setCount, your component function will get called again, and useState will return the updated count.

Live Example:

const {useState} = React;

function Example() {
  const [count, setCount] = useState(false);
  const test = () =>{
    setCount(!count); // Schedules asynchronous call to Example, re-rendering the component
  };
  return (
    <div className="App">
      <h1 onClick={test}>Hello CodeSandbox</h1>
      <div>Count: {String(count)}</div>
    </div>
  );
}

ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

If you need to perform some side-effect when count changes, use useEffect:

useEffect(() => {
  console.log(`count changed to ${count}`);
}, [count]);

Notice that we tell useEffect to only call our callback when count changes, so that if we have other state, our callback doesn't run unnecessarily.

Live Example:

const {useState, useEffect} = React;

function Example() {
  const [count, setCount] = useState(false);
  const test = () =>{
    setCount(!count); // Schedules asynchronous call to Example, re-rendering the component
  };
  useEffect(() => {
    console.log(`count changed to ${count}`);
  }, [count]);
  return (
    <div className="App">
      <h1 onClick={test}>Hello CodeSandbox</h1>
      <div>Count: {String(count)}</div>
    </div>
  );
}

ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

The state update is asynchronous so if you are using Hooks you can use useEffect to be notified after an update.

Here is an example:

https://codesandbox.io/s/wxy342m6l

If you are using setState of a React component, you can use the callback

this.setState((state) => ({count: !state.count}), () => console.log('Updated', this.state.count));

Remember to use the callback to update state if you need a state value.


The setState hook doesn't update the value in the component directly. It updates it's internal state, causes a rerender of the component, and then returns the new state value, which you assign to count.

When you console.log(count) inside test, you display the current value of count, which is the old value (before the update). If you'll move the console.log() to the render, you'll see the new value:

const { useState } = React;

const Component = () => {
  const [count, setCount] = useState(false);

  const test = () => {
    console.log('before: ', count);
    setCount(!count)
  }
  
  console.log('after: ', count);

  return (
    <div className="App">
      <button onClick={test}>Click</button>
    </div>
  );
}

ReactDOM.render(
   <Component />,
   demo
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="demo"></div>