React源码解析之setState和forceUpdate

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)getInstanceweb

//getInstance
export function get(key{
  return key._reactInternalFiber;
}
复制代码

就是获取目标对象的_reactInternalFiber属性,即this.setState中的thisapp

(2)requestCurrentTime,请见:React源码解析之ReactDOM.render()异步

(3)computeExpirationForFiber,请见:React源码解析之ExpirationTime函数

(4)createUpdate,请见:React源码解析之Update和UpdateQueuepost

(5)注意下payloadpayload就是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、综上
执行setStateforUpdateReact进行更新的流程为:
(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)进行任务调度


(完)

相关文章
相关标签/搜索