React State 原理
YuLong~W 人气:0问题:setState 到底是同步还是异步的?
如果对 React 底层有一定了解,可以回答出 batchUpdate 批量更新概念,以及批量更新被打破的条件。
答案:有时是同步,有时是异步。
在 合成事件 和 生命周期函数 里是 异步 的在 原生事件 和 setTimeout、promise
里是 同步 的
造成setState
的异步并不是由内部的异步代码引起的,在本身的执行过程中时同步的,但是合成事件和生命周期函数的调用顺序在更新之前,导致在内部不能直接得到更新后的值,可以用第二个参数 callback 来获取。
具体解释:可参考setState的执行过程
类组件state
setState(obj,callback)
- 第一个参数:当 obj 为一个对象,则为即将合并的 state ;如果 obj 是一个函数,那么当前组件的 state 和 props 将作为参数,返回值用于合并新的 state。
- 第二个参数 callback :callback 为一个函数,函数执行上下文中可以获取当前 setState 更新后的最新 state 的值,可以作为依赖 state 变化的副作用函数,可以用来做一些基于 DOM 的操作。
一次事件中触发一次如上 setState ,在 React 底层执行过程:
render 阶段 render 函数执行 -> commit 阶段真实 DOM 替换 -> setState 回调函数执行 callback
首先,setState 会产生当前更新的优先级(老版本用 expirationTime ,新版本用 lane )。接下来 React 会从 fiber Root 根部 fiber 向下调和子节点,调和阶段将对比发生更新的地方,更新对比 expirationTime ,找到发生更新的组件,合并 state,然后触发 render 函数,得到新的 UI 视图层,完成 render 阶段。接下来到 commit 阶段,commit 阶段,替换真实 DOM ,完成此次更新流程。此时仍然在 commit 阶段,会执行 setState 中 callback 函数,到此为止完成了一次 setState 全过程。
setState原理揭秘
本质:React 底层调用 Updater 对象上的 enqueueSetState 方法
enqueueSetState()
:创建一个update,放入当前 fiber对象 的待更新队列中,最后开启调度更新,进入更新流程。
React 的 batchUpdate 批量更新
目的:多次 setstate 会让逻辑多停留在 js 运行层面,阻塞了浏览器绘制,因此需要批量更新
batchedEventUpdates ()
:
分析流程:
- React 事件执行前通过 isBatchingEventUpdates=true 打开开关,开启事件批量更新
- 当事件结束,通过 isBatchingEventUpdates=false 关闭开关
- 在 scheduleUpdateOnFiber 中根据这个开关来确定是否进行批量更新
1)异步环境下,继续开启批量更新模式:
异步操作里面的批量更新规则会被打破,因此提供了手动批量更新方法: unstable_batchedUpdates
2)提升更新优先级:
提供了方法: flushSync
,可以将回调函数中的更新任务,放在一个较高的优先级中优先执行
补充:flushSync 在同步条件下,会合并之前的 setState | useState
3)总结:React 同一级别更新优先级 关系是:
flushSync 中的 setState > 正常执行上下文中 setState > 异步 setTimeout ,Promise 中的 setState
函数组件state
const [ state , dispatch ] = useState(initData)
- ① state 目的提供给 UI ,作为渲染视图的数据源
- ② dispatch 改变 state 的函数,可以理解为推动函数组件渲染的渲染函数
- ③ initData 初始值
initData的初始值
- 第一种情况是非函数,将作为 state 初始化的值
- 第二种情况是函数,函数的返回值作为 useState 初始化的值
dispatch的参数
- 第一种非函数情况,此时将作为新的值,赋予给 state,作为下一次渲染使用
- 第二种是函数的情况,如果 dispatch 的参数为一个函数,这里可以称它为reducer,reducer 参数,是上一次返回最新的 state,返回值作为新的 state
监听 state 变化
useEffect
:常可以把 state 作为 依赖项 传入 useEffect 第二个参数 deps ,但是注意 useEffect 初始化会默认执行一次
dispatch
更新特点
与类组件一样,但是当调用改变 state 的函数dispatch,在本次函数执行上下文中,是获取不到最新的 state 值的
原因:函数组件更新就是函数的执行,在一次执行过程中,函数内部所有变量重新声明,所以改变的 state 只有在下一次函数执行时才更新。
useState 原理在之后 Hooks 讲解
问:类组件中的 setState
和函数组件中的 useState
有什么异同?
答:相同点:
- 原理:setState 和 useState 更新视图,底层都调用了 scheduleUpdateOnFiber 方法,而且事件驱动情况下都有批量更新规则
- 语法:第一个参数都可以传入函数
不同点:
- 在不是 pureComponent 组件模式下, setState 不会浅比较两次 state 的值。只要调用 setState 就会执行更新。但是 useState 中 dispatchAction 会默认比较两次state是否相同来更新组件
- setState 有专门监听 state 变化的回调函数 callback,可以获取最新 state。但是 useState 只能通过 useEffect 来执行 state 变化引起的副作用
- setState 在底层处理逻辑时将旧 state 进行合并处理,而 useState 是重新赋值
加载全部内容