最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - REACT - Updating state and then doing a console.log , shows unupdated state - Stack Overflow

programmeradmin2浏览0评论

After clicking the button the console shows 0 and the page 1

function App() {
  const [count, setCount] = useState(0);
  
  const addOne = () => {
    setCount(count + 1)

    console.log(count)
  }
  
  return (
    <>
      <p>{count}</p>
      <button onClick={addOne}>Add</button>
    </>
  );
}

I think is because the setCount() is happening asynchronously but even if I add a setTimeout to the console.log(), the console keeps showing the unupdated state

Why???

After clicking the button the console shows 0 and the page 1

function App() {
  const [count, setCount] = useState(0);
  
  const addOne = () => {
    setCount(count + 1)

    console.log(count)
  }
  
  return (
    <>
      <p>{count}</p>
      <button onClick={addOne}>Add</button>
    </>
  );
}

I think is because the setCount() is happening asynchronously but even if I add a setTimeout to the console.log(), the console keeps showing the unupdated state

Why???

Share Improve this question asked Feb 4, 2021 at 4:13 GoodLookingCoderGoodLookingCoder 354 bronze badges 1
  • State changes are asynchronous – Jayce444 Commented Feb 4, 2021 at 4:15
Add a ment  | 

3 Answers 3

Reset to default 6

The state updation in React is always asynchronous. you will find the updated state value of count in useEffect

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

  
  useEffect(()=> {
      console.log('count',count);
  },[count])
  
  const addOne = () => {
    setCount(count + 1)
  } 

  return (
    <>
      <p>{count}</p>
      <button onClick={addOne}>Add</button>
    </>
  );
}

Closures

You are experiencing the unupdated state in the console log, because of closures.

when your function is created when the ponent is rendered, and closure is created with the value of count at the time the closure is created.

if the value of count is 0, and your ponent rerenders, a closure of your function will be created and attached to the event listener of the onlcick.

in that case, the first render of your ponent

 const addOne = () => {
    setCount(count + 1)

    console.log(count)
  }

is equivalent to (replace count with 0)

 const addOne = () => {
    setCount(0 + 1)

    console.log(0)
  }

therefore it makes sense in your case that count is 0 when it is console logged.

In this case, I believe its the closure you are experiencing bined with the asynchronous behavior of setState

Async behaviour

codesandbox

Async behaviour bees a problem when asynchronous actions are occuring. setTimeout is one of the basic async actions. Async actions always require that you provide a function to the setCount function, which will accept the latest state as a parameter, with the nextState being the return value of this function. This will always ensure the current state is used to calculate the next state, regardless of when it is executed asynchronously.

  const addOneAsync = () => {
    setCountAsync((currentState) => {
      const nextState = currentState + 1;
      console.log(`nextState async ${nextState}`);
      return nextState;
    });
  };

I have created a codesandbox demonstrating the importance of this. CLick the "Count" button fast 4 times. (or any number of times) and watch how the count result is incorrect, where the countAsync result is correct.

addOneAsync: when the button is clicked, a closure is created around addOneAsync, but since we are using a function which accepts the currentState, when it eventually fires, the current state will be used to calculate the next state

addOne: When the button is clicked, a closure is created around addOne where count is captured as the value at the time of the click. If you click the count button 4 times before count has increased, you will have 4 closures of addOne set to be fired, where count is captured as 0.

All 4 timeouts will fire and simply set count to 0 + 1, hence the result of 1 for the count.

Yes, you're right about the origins of this behavior and the other posters here seem to have explained how to fix it. However, I don't see the answer to your specific question:

...but even if I add a setTimeout to the console.log(), the console keeps showing the unupdated state Why???

So what you mean is that even if you handle that console.log call like so:

  const addOne = () => {
    setCount((count) => count + 1);
    setTimeout(() => console.log(count), 1000);
  }

It will STILL print the old, un-updated value of count. Why? Shouldn't the timeout allow time for count to update? I will quote the answer:

This is subtle but expected behavior. When setTimeout is scheduled it's using the value of count at the time it was scheduled. It's relying on a closure to access count asynchronously. When the ponent re-renders a new closure is created but that doesn't change the value that was initially closed over.

Source: https://github./facebook/react/issues/14010#issuement-433788147

So there you have it.

发布评论

评论列表(0)

  1. 暂无评论