setState和forceUpdate
Cosen95 opened this issue · comments
setState
setState
的定义在packages/react/src/ReactBaseClasses.js
:
/**
* @param {object|function} partialState Next partial state or function to
* produce next partial state to be merged with current state.
* @param {?function} callback Called after state is updated.
* @final
* @protected
*/
Component.prototype.setState = function (partialState, callback) {
invariant(
typeof partialState === "object" ||
typeof partialState === "function" ||
partialState == null,
"setState(...): takes an object of state variables to update or a " +
"function which returns an object of state variables."
);
this.updater.enqueueSetState(this, partialState, callback, "setState");
};
可以看到内部调用了enqueueSetState
:
// packages/react-reconciler/src/ReactFiberClassComponent.js
// 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();
// 计算fiber对象的过期时间
const expirationTime = computeExpirationForFiber(currentTime, fiber);
// 创建update对象
const update = createUpdate(expirationTime);
// setState传进来的要更新的对象
update.payload = payload;
// callback就是setState({},()=>{})的回调函数
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, "setState");
}
update.callback = callback;
}
flushPassiveEffects();
// update入队
enqueueUpdate(fiber, update);
// 任务调度
scheduleWork(fiber, expirationTime);
},
// ...
};
enqueueSetState
函数的作用是:给React
节点的fiber
对象创建update
,并将该更新对象入队
传入的inst
即调用this.setState
时传进来的this
,也就是classComponent
实例。
通过getInstance
方法来获取目标对象的_reactInternalFiber
属性。
下面依次拿到currentTime
和expirationTime
,然后通过createUpdate
来创建update
对象。
最后update
入队、进入任务调度。
接着来看下forceUpdate
。
forceUpdate
定义同样在packages/react/src/ReactBaseClasses.js
:
/**
* Forces an update. This should only be invoked when it is known with
* certainty that we are **not** in a DOM transaction.
*
* You may want to call this when you know that some deeper aspect of the
* component's state has changed but `setState` was not called.
*
* This will not invoke `shouldComponentUpdate`, but it will invoke
* `componentWillUpdate` and `componentDidUpdate`.
*
* @param {?function} callback Called after update is complete.
* @final
* @protected
*/
Component.prototype.forceUpdate = function (callback) {
this.updater.enqueueForceUpdate(this, callback, "forceUpdate");
};
内部调用了enqueueForceUpdate
方法:
enqueueForceUpdate(inst, callback) {
const fiber = getInstance(inst);
const currentTime = requestCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, fiber);
const update = createUpdate(expirationTime);
update.tag = ForceUpdate;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'forceUpdate');
}
update.callback = callback;
}
flushPassiveEffects();
enqueueUpdate(fiber, update);
scheduleWork(fiber, expirationTime);
},
与enqueueSetState()
方法的流程类似,唯一不同的是多了个手动修改属性tag
的值:
update.tag = ForceUpdate;
可以看到createUpdate()
方法中,初始化的tag
值是UpdateState
:
export const UpdateState = 0; // 更新
export const ReplaceState = 1; // 替换
export const ForceUpdate = 2; // 强制更新
export const CaptureUpdate = 3; // 捕获性的更新
export function createUpdate(expirationTime: ExpirationTime): Update<*> {
return {
expirationTime: expirationTime,
// 重点提下CaptureUpdate,在React16后有一个ErrorBoundaries功能
// 即在渲染过程中报错了,可以选择新的渲染状态(提示有错误的状态),来更新页面
// 默认是0即更新
tag: UpdateState,
payload: null,
callback: null,
next: null,
nextEffect: null,
};
}
因此要改成ForceUpdate
,以便React
进行Update
优先级排序。