1、enqueueSetState()
非异步方法中,不管调用多少个setState
,它们都会在最后一次setState
后,放入更新队列,而后执行一次统一的更新,详情请参考:
React.setState之state批处理的机制 和 为何React.setState是异步的? javascript
做用:
给React
节点的fiber
对象建立update
,并将该更新对象入队java
源码:react
//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
web
//getInstance
export function get(key) {
return key._reactInternalFiber;
}
复制代码
就是获取目标对象的_reactInternalFiber
属性,即this.setState
中的this
:app
(2)requestCurrentTime
,请见:React源码解析之ReactDOM.render()异步
(3)computeExpirationForFiber
,请见:React源码解析之ExpirationTime函数
(4)createUpdate
,请见:React源码解析之Update和UpdateQueuepost
(5)注意下payload
,payload
就是setState
传进来的要更新的对象this
this.setState({a:1},callback) 中的 {a:1} 即 payload
//====================
update.payload = payload;
复制代码
(6)enqueueUpdate
,请见:React源码解析之Update和UpdateQueuespa
(7)scheduleWork
,篇幅较长,会放在下篇讲。
2、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
优先级排序
3、综上
执行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)进行任务调度
(完)