多个"setState"未同步-如果使用从"await"获得的值



我发现,如果:

  1. 有两个setState
  2. 使用从await获得的值调用第一个
  3. 两者在同一线程中调用

然后,有一个帧,其中一个状态被设置,但另一个没有。

示例:

const [bool1, setBool1] = useState(false)
const [bool2, setBool2] = useState(false)
useEffect(() => {
(async () => {
const _bool1 = await true
setBool1(_bool1)
const _bool2 = true
setBool2(_bool2)
})()
}, [])
console.log(`bool1: ${bool1}, bool2: ${bool2}`)

实时示例(打开浏览器控制台进行输出(:

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
<script type="text/babel">
const {useState, useEffect} = React;
const App = () => {
const [bool1, setBool1] = useState(false)
const [bool2, setBool2] = useState(false)
useEffect(() => {
(async () => {
const _bool1 = await true;
setBool1(_bool1);
const _bool2 = true;
setBool2(_bool2);
})();
}, [])
console.log(`bool1: ${bool1}, bool2: ${bool2}`);
return <p>See browser console for output</p>;
}

ReactDOM.render(<App />, document.body);
</script>

输出为:

bool1: false, bool2: false
bool1: true, bool2: false
bool1: true, bool2: true

在这里,第二行令人困惑——我觉得不应该有bool1bool2不具有相同值的帧。我知道setState通常不会在同一帧中设置值,但在这种情况下,这一事实真的相关吗?

更令人困惑的是,如果1。不满足,即,如果我为setBool1移除await,则第二行消失。在这种情况下,我不明白await是如何破坏setBool1setBool2之间的同步的——因为我认为在调用setBool1时,promise已经得到了重新解析,所以应该没有什么区别。

useState钩子触发重新渲染。因此,由于异步状态更新不会对其进行批处理,因此当执行setBool1(_bool1)时,组件将使用新的bool1值重新渲染,然后继续使用下一个setBool2(_bool2),这就是为什么您会看到这种情况按顺序发生:

bool1: false, bool2: false
bool1: true, bool2: false
bool1: true, bool2: true

什么是批处理

React功能将所有状态更新合并为单个更新,导致单个重新渲染,从而改进应用程序的性能。在React的早期版本中,批处理仅对事件处理程序执行。

但是

在异步状态更新的情况下,状态更新不会被批处理

在这里你可以找到一个很好的、广泛的解释https://blog.saeloun.com/2021/07/22/react-automatic-batching.html

更多信息:https://github.com/reactwg/react-18/discussions/21

这应该随着React 18而改变

使用createRoot在React 18中启动,所有更新都将自动批处理,无论它们来自何处。

最新更新