React源码解析之setState和forceUpdate
一、enqueueSetState()
非异步方法中,无论调用多少个setState
,它们都会在最后一次setState
后,放入更新队列,然后执行一次统一的更新,详情请参考:
React.setState之state批处理的机制 和 为什么React.setState是异步的?
作用:
给React
节点的fiber
对象创建update
,并将该更新对象入队
源码:
//classComponent初始化的时候拿到的update对象 const classComponentUpdater = { isMounted, enqueueSetState(inst, payload, callback) { //inst即调用this.setState时传进来的this //也就是classComponent实例 //通过this获取fiber对象 //this._reactInternalFiber //this本身有存储 fiber对象 的属性,叫 _reactInternalFiber const fiber = getInstance(inst); //计算当前时间,之前讲过 不讲了 const currentTime = requestCurrentTime(); //异步加载的设置,暂时不讲 const suspenseConfig = requestCurrentSuspenseConfig(); //计算fiber对象的过期时间 const expirationTime = computeExpirationForFiber( currentTime, fiber, suspenseConfig, ); //创建update对象 const update = createUpdate(expirationTime, suspenseConfig); //setState传进来的要更新的对象 update.payload = payload; //callback就是setState({},()=>{})的回调函数 if (callback !== undefined && callback !== null) { if (__DEV__) { warnOnInvalidCallback(callback, 'setState'); } update.callback = callback; } //暂时不管 if (revertPassiveEffectsChange) { flushPassiveEffects(); } //update入队 enqueueUpdate(fiber, update); //任务调度 scheduleWork(fiber, expirationTime); }, };
解析:
(1)getInstance
//getInstance export function get(key) { return key._reactInternalFiber; }
就是获取目标对象的_reactInternalFiber
属性,即this.setState
中的this
:
(2)requestCurrentTime
,请见:React源码解析之ReactDOM.render()
(3)computeExpirationForFiber
,请见:React源码解析之ExpirationTime
(4)createUpdate
,请见:React源码解析之Update和UpdateQueue
(5)注意下payload
,payload
就是setState
传进来的要更新的对象
this.setState({a:1},callback) 中的 {a:1} 即 payload //==================== update.payload = payload;
(6)enqueueUpdate
,请见:React源码解析之Update和UpdateQueue
(7)scheduleWork
,篇幅较长,会放在下篇讲。
二、enqueueForceUpdate()
作用:
强制让组件重新渲染,也是给React节点的fiber对象创建update,并将该更新对象入队
源码:
enqueueForceUpdate(inst, callback) { const fiber = getInstance(inst); const currentTime = requestCurrentTime(); const suspenseConfig = requestCurrentSuspenseConfig(); const expirationTime = computeExpirationForFiber( currentTime, fiber, suspenseConfig, ); const update = createUpdate(expirationTime, suspenseConfig); //与setState不同的地方 //默认是0更新,需要改成2强制更新 update.tag = ForceUpdate; if (callback !== undefined && callback !== null) { if (__DEV__) { warnOnInvalidCallback(callback, 'forceUpdate'); } update.callback = callback; } if (revertPassiveEffectsChange) { flushPassiveEffects(); } enqueueUpdate(fiber, update); scheduleWork(fiber, expirationTime); },
解析:
与enqueueSetState()
方法的流程类似,唯一不同的是多了个手动修改属性tag
的值:
//与setState不同的地方 //默认是0更新,需要改成2强制更新 update.tag = ForceUpdate;
可以看到createUpdate()
方法中,初始化的tag
值是UpdateState
:
//创建update对象 export function createUpdate( expirationTime: ExpirationTime, suspenseConfig: null | SuspenseConfig, ): Update<*> { return { // export const UpdateState = 0; // export const ReplaceState = 1; // export const ForceUpdate = 2; // export const CaptureUpdate = 3; //重点提下CaptureUpdate,在React16后有一个ErrorBoundaries功能 //即在渲染过程中报错了,可以选择新的渲染状态(提示有错误的状态),来更新页面 //默认是0即更新 tag: UpdateState, //0更新 1替换 2强制更新 3捕获性的更新 }; }
因此要改成ForceUpdate
,以便React
进行update
优先级排序
三、综上
执行setState
或forUpdate
后React
进行更新的流程为:
(1)获取this
上的fiber
对象
(2)计算currentTime
(3)根据(1)fiber
和(2)currentTime
计算fiber
对象的expirationTime
(4)根据(3)expirationTime
创建update
对象
(5)将setState
中要更新的对象赋值到(4)update.payload
(6)将setState
中要执行的callback
赋值到(4)update.callback
(7)update
入队updateQueue
(8)进行任务调度
(完)