1. setState 是同步还是异步
要想回答这个问题,首先先看例子,
import React, { Component } from 'react'
import './App.css';
class ClsApp extends Component {
state = {
num: 0
}
updateNum = () => {
console.log('setState 前',this.state.num)
this.setState({ num: this.state.num + 1 })
console.log('setState 后 -- 正常',this.state)
// setTimeout(() => {
// this.setState({ num: this.state.num + 1 })
// console.log('setState 后 -- setTimeout',this.state)
// })
}
render() {
return (
<>
<button onClick={this.updateNum}>+1</button>
<p>{this.state.num}</p>
</>
)
}
}
export default ClsApp
在这个例子中,正常点击触发更新,this.state 的值前后一致,我们说它是异步的,而如果放在 setTimeout 中,前后不一致,我们说它是同步的,那么到底是怎么肥事呢?
考虑 setState 是同步还是异步是没有意义的
- 回到上面这个例子,无论是同步还是异步,我们都能在视图中得到我们想要的更新过后的结果,而当我们需要做一些依赖更新过后的值的操作,本身就不应该编写在 setState 之后。
- 如果我们想要依赖更新之后的状态值
- 在 ClassComponent 中,我们可以在 componentDidMounted 或者 componentDidUpdate 中执行
- 在 FunctionComponent 中,我们可以在 useEffect 的回调函数中执行
- 在业务中一旦需要考虑这个特性,大概率都是写法出问题了,推荐按照正规的写法重新编码,而不是在纠结它的同步异步特性。
React 在不同 mode 下,对 setState 触发的更新有不同的处理
legacy模式 -- ReactDOM.render
- 默认情况下使用 useSate 的是异步的,原因是 React 内部有一个
性能优化机制 -- batchedUpdates 批处理
- 这个性能优化机制是为了多次调用 setState 触发更新的时候,合并成一个更新,减少因状态更新引擎的页面渲染过多而导致的性能问题。
- 在 react 的声明周期函数中调用 setState,会执行 batchedUpdates,这个时候会首先加入一个 batchedContext 的标志,有这个标志就会使得 setState 的 action 放入到 batchedUpdateQueue 中
- 因此,调用 setState 之后,不会立刻执行更新,而是要将(1-n个) useState 的 action 放在 batchedUpdateQueue 队列中的。等到 react 上下文结束了,又会取消 batched 标志,这个时候就同步执行响应的回调了。
- 当我们用 setTimeout 包裹 setState 的时候,当执行的时候,不会走 react 内部的
batchedUpdates 优化机制
, 此时运行上下文为noContext
;在调度更新过程中,如果是同步优先级 + noContext 上下文
,就会执行同步执行回调队列flushSyncCallbackQueue
. - 因此在 legacy 模式下,如果是在可以触发 react 机制的位置上使用 setState, 是异步执行的,如果是 setTimeout 这种不会触发 react 机制的上下包裹下使用 setState,是同步的。
Concurrent 模式下 -- 暂时还属于试验阶段, ReactDOM.unstable_createRoot().render()
- 在 legacy 中,使用 setTimeout 包裹 setState 会是同步执行的,原因是 legacy 模式下,FiberRoot 对应的 lane 是 Synclane 同步优先级
- 在同步优先级的时候,如果上下文是 noContext,才会同步执行
- 而在 Concurrent 模式下,是异步优先级,所以所有的更新都是异步的
- 因此无论怎么处理,使用 setState 触发的更新都是异步的。
小结
- Legacy 模式下,命中 batchUpdates 时是异步的,未命中时是同步的
- 在 Concurrent 模式下都是异步的
参考
- 可以参考 WorkLoop 源码文件中的
scheduleUpdateOnFiber
函数进行进一步的学习 - 卡颂老师关于 setState 问题的视频
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!