简体   繁体   English

React - useState 钩子第一次与后续时间设置状态时的奇怪行为

[英]React - weird behavior when useState hook sets state for the first time vs subsequent times

consider this code:考虑这个代码:

 const {useState} = React; function App() { const [count, setCount] = useState(0); const onClick = () => { setCount((prevCount) => { console.log(prevCount + 1); return prevCount + 1; }); setCount((prevCount) => { console.log(prevCount + 1); return prevCount + 1; }); setCount((prevCount) => { console.log(prevCount + 1); return prevCount + 1; }); console.log("onclick"); }; console.log("rendering"); return <button onClick={onClick}> Increment {count} </button>; } ReactDOM.render(<App/>, document.getElementById('app'))
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script> <div id="app"></div>

When run and the button is clicked, this is the output it produces:当运行并单击按钮时,这是它产生的输出:

1
onclick 
2
3
rendering 

I would have expected the output to be我原以为输出是

onclick 
1
2
3
rendering

as I am using the updater function to access previous stat and state updates are batched and async by default.因为我正在使用更新程序功能来访问以前的统计信息,并且默认情况下状态更新是批处理和异步的。

And expectedly so, further click on the button confirm this, and produce this output:正如预期的那样,进一步单击按钮确认这一点,并产生以下输出:

onclick 
4
5
6
rendering

I suspect that the first set state is always synchronous in case of hooks, because if I change my code to this:我怀疑第一个设置状态在钩子的情况下总是同步的,因为如果我将代码更改为:

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

  const onClick = () => {
    // add this extra set state before any other state updates
    setCount(1);
    setCount((prevCount) => {
      console.log(prevCount + 1);
      return prevCount + 1;
    });

    setCount((prevCount) => {
      console.log(prevCount + 1);
      return prevCount + 1;
    });

    setCount((prevCount) => {
      console.log(prevCount + 1);
      return prevCount + 1;
    });

    console.log("onclick");
  };

  console.log("rendering");

  return <button onClick={onClick}> Increment {count} </button>;
}

the output as expected is:预期的输出是:

onclick 
2
3
4
rendering 

I haven't been able to find any explanations for this online yet.我还没有在网上找到对此的任何解释。 This would not impact any functionality as much as I can see, because although the first time it is executed synchronously it still updates the state asynchronously, which means I can't access the updated state in console.log("onclick" + count)这不会像我看到的那样影响任何功能,因为尽管第一次同步执行它仍然异步更新状态,这意味着我无法访问 console.log("onclick" + count) 中的更新状态

Would be helpful to get an explanation of why it works like this.将有助于解释为什么它会像这样工作。

NOTE: discussed this on github.注意:在 github 上讨论过这个。 Seems like this is one of the things that as consumers we should not care about.似乎这是我们作为消费者不应该关心的事情之一。 It is an implementation detail.这是一个实现细节。 https://github.com/facebook/react/issues/19697 https://github.com/facebook/react/issues/19697

React will merge all state changes into one update to reduce rendering as rendering costs a lot, setState is not surely updated when you call log. React 会将所有状态更改合并为一次更新以减少渲染,因为渲染成本setState ,调用 log 时setState不一定会更新。 So you can just log("count="+(count+3)) or use useEffect .所以你可以只log("count="+(count+3))或使用useEffect

useEffect sets a callback when state/prop change; useEffect在 state/prop 改变时设置回调; the following snippet shows how to log count every time it changes.以下代码段显示了如何在每次更改时记录count You can reference the docs for more information.您可以参考文档以获取更多信息。

 const {useState,useEffect} = React; function App() { const [count, setCount] = useState(0); useEffect(()=>{ console.log("effect!count=" + count) },[count]) const onClick = () => { setCount((prevCount) => { console.log(prevCount + 1); return prevCount + 1; }); setCount((prevCount) => { console.log(prevCount + 1); return prevCount + 1; }); setCount((prevCount) => { console.log(prevCount + 1); return prevCount + 1; }); console.log("onclick"); }; console.log("rendering"); return <button onClick={onClick}> Increment {count} </button>; } ReactDOM.render(<App/>, document.getElementById('app'))
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script> <div id="app"></div>

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM