Cosen95 / blog

关注行业前沿,分享所见所学。持续输出优质文章 :rocket:
212 stars 15 forks source link

setState和forceUpdate #56

Open Cosen95 opened 4 years ago

Cosen95 commented 4 years ago

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属性。

下面依次拿到currentTimeexpirationTime,然后通过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优先级排序。