1. 前言
- React 中 setState 什么时候是同步的,什么时候是异步的
- 这个问题之前不少道友问过,因为在我们的印象中这个都是当做异步来解释的
- 今天就来深入下,玩玩
2. react18之前
- setState在不同情况下可以表现为异步或同步
- 在
Promise
的状态更新、js原生
事件、setTimeout、setInterval中是同步的。- 在react的
合成事件
中,是异步
的
3. react18之后。
- setState都会表现为异步(即批处理)。
- 批处理:是指 React将多个状态更新分组到单个重新渲染中以获得更好的性能
- 如果同一点击事件中有两个状态更新,React 总是将它们批处理为一次重新渲染。如果运行以下代码,您将看到每次单击时,尽管您设置了两次状态,React 只执行一次渲染
function App() { const [count, setCount] = useState(0); const [flag, setFlag] = useState(false); function handleClick() { setCount(c => c + 1); // Does not re-render yet setFlag(f => !f); // Does not re-render yet // React will only re-render once at the end (that's batching!) } return ( <div> <button onClick={handleClick}>Next</button> <h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1> </div> ); }
4. react18之前 为啥不确定同步异步呢
- 在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.state 。
- 所谓“除此之外”,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用
- 原因:
- 在React的setState函数实现中,会根据一个变量
isBatchingUpdates
判断是直接更新this.state还是放到队列中回头再说,- 而
isBatchingUpdates
默认是false,也就表示setState会同步更新this.state,- 但是,有一个函数
batchedUpdates
,这个函数会把isBatchingUpdates修改为true,- 而当React在调用事件处理函数之前就会调用这个
batchedUpdates
,造成的后果,就是由React控制的事件处理过程setState不会同步更新this.state。
- 注意:
- setState的“异步”并不是说内部由异步代码实现,其实本身
执行
的过程和代码都是同步
的,- 只是
合成事件
和钩子
函数的调用顺序在更新之前
,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”,
3 .当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。
- 总结
- setState 只在合成事件和 hook() 中是“异步”的,在 原生事件和 setTimeout 中都是同步的。
参考资料
react18 批处理减少渲染次数 官方交流多看看