我发现,如果:
- 有两个
setState
- 使用从
await
获得的值调用第一个 - 两者在同一线程中调用
然后,有一个帧,其中一个状态被设置,但另一个没有。
示例:
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
在这里,第二行令人困惑——我觉得不应该有bool1
和bool2
不具有相同值的帧。我知道setState
通常不会在同一帧中设置值,但在这种情况下,这一事实真的相关吗?
更令人困惑的是,如果1。不满足,即,如果我为setBool1
移除await
,则第二行消失。在这种情况下,我不明白await
是如何破坏setBool1
和setBool2
之间的同步的——因为我认为在调用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中启动,所有更新都将自动批处理,无论它们来自何处。