简体   繁体   English

反应 - 更新 state 然后执行 console.log,显示未更新的 state

[英]REACT - Updating state and then doing a console.log , shows unupdated state

After clicking the button the console shows 0 and the page 1单击按钮后,控制台显示 0 和页面 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我认为是因为setCount()是异步发生的,但即使我将 setTimeout 添加到console.log() ,控制台也会继续显示未更新的 state

Why???为什么???

The state updation in React is always asynchronous. React 中的 state 更新始终是异步的。 you will find the updated state value of count in useEffect您会在 useEffect 中找到更新后的 state 计数值

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 .由于关闭,您在控制台日志中遇到未更新的 state 。

when your function is created when the component is rendered, and closure is created with the value of count at the time the closure is created.当您的 function 在渲染组件时创建时,并且在创建闭包时使用 count 的值创建闭包。

if the value of count is 0, and your component rerenders, a closure of your function will be created and attached to the event listener of the onlcick.如果 count 的值为 0,并且您的组件重新呈现,则会创建 function 的闭包并将其附加到 onlcick 的事件侦听器。

in that case, the first render of your component在这种情况下,您的组件的第一次渲染

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

    console.log(count)
  }

is equivalent to (replace count with 0)相当于(将计数替换为 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.因此,在您的情况下,控制台记录时计数为 0 是有意义的。

In this case, I believe its the closure you are experiencing combined with the asynchronous behavior of setState在这种情况下,我相信您遇到的关闭与 setState 的异步行为相结合

Async behaviour异步行为

codesandbox 密码箱

Async behaviour becomes a problem when asynchronous actions are occuring.当异步操作发生时,异步行为会成为一个问题。 setTimeout is one of the basic async actions. setTimeout 是基本的异步操作之一。 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. 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.这将始终确保当前 state 用于计算下一个 state,无论何时异步执行。

  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.快速单击“计数”按钮 4 次。 (or any number of times) and watch how the count result is incorrect, where the countAsync result is correct. (或任意次数)并观察计数结果如何不正确,countAsync 结果在哪里正确。

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 addOneAsync:单击按钮时,会在addOneAsync周围创建一个闭包,但由于我们使用的是接受 currentState 的 function,因此当它最终触发时,当前的 state 将用于计算下一个 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. addOne:单击按钮时,会在addOne周围创建一个闭包,其中 count 被捕获为单击时的值。 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.如果在 count 增加之前单击 count 按钮 4 次,您将触发 4 个 addOne 闭包,其中 count 被捕获为 0。

All 4 timeouts will fire and simply set count to 0 + 1, hence the result of 1 for the count.所有 4 次超时都将触发并简单地将计数设置为 0 + 1,因此计数的结果为 1。

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??? ...但是即使我在 console.log() 中添加了 setTimeout,控制台仍然显示未更新的 state 为什么?

So what you mean is that even if you handle that console.log call like so:所以你的意思是,即使你像这样处理 console.log 调用:

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

It will STILL print the old, un-updated value of count .它仍然会打印旧的、未更新的count值。 Why?为什么? Shouldn't the timeout allow time for count to update?超时不应该允许count更新吗? 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.安排 setTimeout 时,它使用安排时的count It's relying on a closure to access count asynchronously.它依靠闭包来异步访问计数。 When the component re-renders a new closure is created but that doesn't change the value that was initially closed over.当组件重新渲染时,会创建一个新的闭包,但这不会改变最初关闭的值。

Source: https://github.com/facebook/react/issues/14010#issuecomment-433788147来源: https://github.com/facebook/react/issues/14010#issuecomment-433788147

So there you have it.所以你有它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 React state 未定义,即使 console.log 显示它 - React state is undefined , even though console.log shows it 反应状态未在应用程序中更新,但我可以console.log正确更新状态 - React state not updating in app but I can console.log correct updated state React state 值在通过 console.log() 打印时显示,但不在网页上 - React state value shows when printed via console.log(), but not on the webpage 函数状态的console.log - Console.log of function state 输入字段未从 redux state 更新,但在 console.log 上显示值反应 - Input field not updating from redux state but displays value on console.log in react Console.log 只显示原始 state,尽管我 100% 确定 state 正在 React 中更新 - Console.log only ever shows the original state, even though I'm 100% sure the state is being updated in React 使用钩子在 console.log() 上反应无限状态,这是为什么? - React infinity state on console.log() using hooks why is that? 反应子组件 state 未定义,但可以使用 console.log 看到 state - React child components state is undefined but can see state using console.log fetchById 无法在反应中呈现 state 但 console.log 正确显示 state - fetchById could not render state in react but console.log presents state correctly DOM /页面未更新,但console.log显示更新后的值 - DOM/Page not updating but console.log shows updated value
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM