共计 3077 个字符,预计需要花费 8 分钟才能阅读完成。
一、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)进行任务调度
(完)