React 源码解读之class组件更新 updateClassComponent (二)

785 阅读18分钟

这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」。

react 版本:v17.0.3

ClassComponent的三种更新情形

在《React 源码解读之class组件更新updateClassComponent (一)》一文中我们提到,对于 ClassComponent 的更新,有三种情形:

  • 情形一:ClassComponent实例未被创建,此时会调用 constructClassInstance 方法构建class组件实例,然后调用 mountClassInstance 方法挂载class组件,并将 shouldUpdate 置为 true,标记组件需要更新渲染。

  • 情形二:ClassComponent实例已经存在,但current(当前渲染在界面上的fiber树) 为null,即 ClassComponent 是初次渲染,此时调用 resumeMountClassInstance 方法,复用ClassComponent实例,并更新 state/props,然后会执行 componentWillMount 生命周期函数。

  • 情形三:ClassComponent实例已经存在,且已经是多次渲染,此时调用 updateClassInstance 方法执行更新操作,且会执行 componentWillUpdate 生命周期函数。

在本文中,我们将对 ClassComponent 更新的第一种情形进行解读。

// react-reconciler/src/ReactFiberBeginWork.new.js

// 组件实例还未创建
if (instance === null) {
  if (current !== null) {
    current.alternate = null;
    workInProgress.alternate = null;
    // Since this is conceptually a new fiber, schedule a Placement effect
    workInProgress.flags |= Placement;
  }

  // 构造 class组件实例
  constructClassInstance(workInProgress, Component, nextProps);
  
  // 挂载 class组件
  mountClassInstance(workInProgress, Component, nextProps, renderLanes);
  
  // shouldUpdate 用于标记组件是否需要更新
  // 由于组件是是首次构建,但还未渲染,因此设置为true,标记组件需要更新渲染
  shouldUpdate = true;
}

情形一:ClassComponent实例未被创建

在 ClassComponent 更新的第一种情形中,首先会调用 constructClassInstance 方法构建class组件实例,然后再调用 mountClassInstance 方法挂载class组件。

我们先来看看class组件实例是如何构建出来的。

构建class组件实例 -- constructClassInstance

//react-reconciler/src/ReactFiberClassComponent.new.js

function constructClassInstance(
  workInProgress: Fiber,
  ctor: any,
  props: any,
): any {
  let isLegacyContextConsumer = false;
  let unmaskedContext = emptyContextObject;
  let context = emptyContextObject;
  const contextType = ctor.contextType;

  // ...

  // ctor 即 workInProgress.type,也就是我们开发中定义class组件时的那个 类
  let instance = new ctor(props, context);
  
  // ...

  // instance.state 是我们在开发中的 class组件中的 this.state
  const state = (workInProgress.memoizedState =
    instance.state !== null && instance.state !== undefined
      ? instance.state
      : null);

  // 初始化 class组件实例,即初始化 workInProgress 和 instance
  adoptClassInstance(workInProgress, instance);
  
  //...

  return instance;
}

在 constructClassInstance 函数中,主要做了一件事情,那就是实例化我们在开发中定义的类组件,然后调用 adoptClassInstance 方法初始化 class组件实例,把class组件实例添加到 workInProgress 节点上。

adoptClassInstance

// react-reconciler/src/ReactFiberClassComponent.new.js
function adoptClassInstance(workInProgress: Fiber, instance: any): void {
  // 将类组件的 updater 对象赋值给实例的 updater
  instance.updater = classComponentUpdater;
  
  // 初次渲染或者更新,要把实例挂在fiber节点的stateNode 上
  workInProgress.stateNode = instance;
  
  // The instance needs access to the fiber so that it can schedule updates
  // 将 fiber 节点挂在 实例的_reactInternals 属性上
  setInstance(instance, workInProgress);
  if (__DEV__) {
    instance._reactInternalInstance = fakeInternalInstance;
  }
}

在 adoptClassInstance 函数中,主要做了三件事情:

  • 第一件事,将 ClassComponent 的 updater对象 (classComponentUpdater)赋值给 class组件实例的 updater。

    // 将类组件的 updater 对象赋值给实例的 updater
    instance.updater = classComponentUpdater;
    
  • 第二件事,无论组件是初次渲染还是更新,都把class组件实例挂在fiber节点的 stateNode 上。

    // 初次渲染或者更新,要把实例挂在fiber节点的stateNode 上
    workInProgress.stateNode = instance;
    
  • 第三件事,调用 setInstance 方法,将 workInProgress 挂在class组件实例的 _reactInternals 属性上,这样就可以通过class组件实例找到当前工作的fiber节点workInProgress了。

    // 将 fiber 节点挂在 实例的_reactInternals 属性上
    setInstance(instance, workInProgress);
    

setInstance 方法,也就是 ReactInstanceMap.js 文件中的 set 方法:

// shared/ReactInstanceMap.js

export function set(key, value) {
  key._reactInternals = value;
}

接下来我们看看这个 classComponentUpdater 对象。

classComponentUpdater

classComponentUpdater 是class组件在初始化的时候拿到的 updater 对象:

const classComponentUpdater = {
  // 判断组件的挂载状态
  isMounted,

  // 对应 setState() 方法  setState 会进行 state 对象合并,只会更新要修改的 key

  // inst 即调用 this.setState 时传递进来的this,也就是 class组件实例

  // payload 即调用 this.setState 时传递进来的初始state,

  // 可以是一个Object对象,也可以是一个function,该function返回一个Object对象
  // callback 即调用 this.setState 时传递进来的 回调函数,该回调函数是可选的
  // 如果我们想在调用 setState 后立即读取 this.state,就可以使用在 callback 中获取
  enqueueSetState(inst, payload, callback) {
    // 通过 class组件实例获取 fiber 对象
    // this._reactInternals 在 this 上通过 _reactInternals 属性存储fiber对象
    const fiber = getInstance(inst);
    // 获取当前时间,通过 performance.now() 或 Date.now() 获取的秒数
    const eventTime = requestEventTime();
    // 创建一个优先级变量(lane模型,通常称为车道模型)
    const lane = requestUpdateLane(fiber);
    // 创建一个 update 对象
    const update = createUpdate(eventTime, lane);
    // 将 stateState 传进来的要更新的对象添加到 update 上
    update.payload = payload;
    if (callback !== undefined && callback !== null) {
      if (__DEV__) {
        warnOnInvalidCallback(callback, 'setState');
      }
      // 将 setState 的第二个参数 callback 添加到 update 对象上
      update.callback = callback;
    }
    // 将新建的 update 添加到 update链表中
    enqueueUpdate(fiber, update, lane);
    // 进入任务调度流程
    const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
    if (root !== null) {
      entangleTransitions(root, fiber, lane);
    }

    // ...

    if (enableSchedulingProfiler) {
      markStateUpdateScheduled(fiber, lane);
    }
  },

  // 对应 replaceState() 方法 
  enqueueReplaceState(inst, payload, callback) {
    const fiber = getInstance(inst);
    const eventTime = requestEventTime();
    const lane = requestUpdateLane(fiber);

    const update = createUpdate(eventTime, lane);
    update.tag = ReplaceState;
    update.payload = payload;

    if (callback !== undefined && callback !== null) {
      if (__DEV__) {
        warnOnInvalidCallback(callback, 'replaceState');
      }
      update.callback = callback;
    }

    enqueueUpdate(fiber, update, lane);
    const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
    if (root !== null) {
      entangleTransitions(root, fiber, lane);
    }

    // ...

    if (enableSchedulingProfiler) {
      markStateUpdateScheduled(fiber, lane);
    }
  },

  /**
   * enqueueForceUpdate 调用 forceUpdate 时实际调用的更新方法
   */  
  enqueueForceUpdate(inst, callback) {
    // 通过 class组件实例获取 fiber 对象
    // this._reactInternals 在 this 上通过 _reactInternals 属性存储fiber对象
    const fiber = getInstance(inst);
    // 获取当前时间,通过 performance.now() 或 Date.now() 获取的秒数
    const eventTime = requestEventTime();
    // 创建一个优先级变量(lane模型,通常称为车道模型)
    const lane = requestUpdateLane(fiber);
    // 创建一个 update 对象
    const update = createUpdate(eventTime, lane);

    // 与 setState 不同的地方
    // update.tag 默认为 0,即更新,将其改成 2 ,即需要强制更新
    update.tag = ForceUpdate;

    if (callback !== undefined && callback !== null) {
      if (__DEV__) {
        warnOnInvalidCallback(callback, 'forceUpdate');
      }
      // 将 setState 的第二个参数 callback 添加到 update 对象上
      update.callback = callback;
    }
    // 将新建的 update 添加到 update链表中
    enqueueUpdate(fiber, update, lane);
    // 进入任务调度流程
    const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
    if (root !== null) {
      entangleTransitions(root, fiber, lane);
    }

    // ...

    if (enableSchedulingProfiler) {
      markForceUpdateScheduled(fiber, lane);
    }
  },
};

可以看到,classComponentUpdater对象中主要有enqueueSetState、enqueueReplaceState、enqueueForceUpdate三个方法,它们都与触发更新有关:

  • enqueueSetState 方法创建了一个 update.tag 为 UpdateState 的update对象,对应着React的API setState() 方法。

  • enqueueReplaceState 方法创建了一个 update.tag 为 ReplaceState 的update对象,对应着React的API replaceState() 方法。

  • enqueueForceUpdate 方法创建了一个 update.tag 为 ForceUpdate 的update对象,对应着React的API forceUpdate() 方法。

从这三个方法的源码我们可以看出,它们的更新流程是相似的:

  1. 获取ClassComponent的this对象上的fiber对象

  2. 计算当前时间和更新优先级

  3. 根据当前时间和更新优先级创建update对象

  4. 将setState中要更新的state对象和要执行的callback添加到update对象上

  5. 将当前update对象加入到updateQueue队列中

  6. 执行scheduleUpdateOnFiber进入任务调度流程

class组件实例构建完成后,接下来就是在还未渲染的class组件实例上调用生命周期函数了,下面我们来看看 mountClassInstance 函数。

挂载class组件 -- mountClassInstance

// react-reconciler/src/ReactFiberClassComponent.new.js

function mountClassInstance(
  workInProgress: Fiber,
  ctor: any,
  newProps: any,
  renderLanes: Lanes,
): void {

  // ...

  // 在 adoptClassInstance 函数里,将 class组件实例 挂载到了 workInProgress 的 stateNode 属性上
  // 因此这里可以通过 workInProgress 的 stateNode 属性获取 class组件实例
  const instance = workInProgress.stateNode;

  // 更新 props/state
  instance.props = newProps;
  instance.state = workInProgress.memoizedState;
  instance.refs = emptyRefsObject;

  // 初始化更新队列
  initializeUpdateQueue(workInProgress);

  // ctor 即 workInProgress.type,也就是我们开发中定义class组件时的那个 类
  const contextType = ctor.contextType;
  // 初始化 context
  if (typeof contextType === 'object' && contextType !== null) {
    instance.context = readContext(contextType);
  } else if (disableLegacyContext) {
    instance.context = emptyContextObject;
  } else {
    const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
    instance.context = getMaskedContext(workInProgress, unmaskedContext);
  }

  // ...

  // 执行更新 updateQueue
  processUpdateQueue(workInProgress, newProps, instance, renderLanes);
  // 在 processUpdateQueue 里 更新了 state,所以 instance 的 state 也需要更新
  instance.state = workInProgress.memoizedState;

  // getDerivedStateFromProps 是 React 新的生命周期函数
  // https://zh-hans.reactjs.org/docs/react-component.html#static-getderivedstatefromprops
  const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
  if (typeof getDerivedStateFromProps === 'function') {
    // 执行 getDerivedStateFromProps 生命周期函数,并更新 state
    applyDerivedStateFromProps(
      workInProgress,
      ctor,
      getDerivedStateFromProps,
      newProps,
    );
    // 更新 instance 的 state
    instance.state = workInProgress.memoizedState;
  }

  // In order to support react-lifecycles-compat polyfilled components,
  // Unsafe lifecycles should not be invoked for components using the new APIs.

  // 判断是否要调用 componentWillMount
  // 如果是第一次渲染,是要执行 componentWillMount 的
  if (
    typeof ctor.getDerivedStateFromProps !== 'function' &&
    typeof instance.getSnapshotBeforeUpdate !== 'function' &&
    (typeof instance.UNSAFE_componentWillMount === 'function' ||
      typeof instance.componentWillMount === 'function')
  ) {
    // 执行 componentWillMount 生命周期函数
    callComponentWillMount(workInProgress, instance);
    // If we had additional state updates during this life-cycle, let's
    // process them now.

    // 在 componentWillMount 钩子函数中是有可能执行 setState 的,
    // 所以要执行更新 updateQueue,并更新 state
    processUpdateQueue(workInProgress, newProps, instance, renderLanes);

    // 在 processUpdateQueue 中更新了 state,因此 instance 的 state 也要更新
    instance.state = workInProgress.memoizedState;
  }

  // 添加标记,等到组件真正渲染到 DOM 上的时候,再去调用 componentDidMount 钩子函数
  if (typeof instance.componentDidMount === 'function') {
    let fiberFlags: Flags = Update;
    if (enableSuspenseLayoutEffectSemantics) {
      fiberFlags |= LayoutStatic;
    }
    
    // ...
    
    workInProgress.flags |= fiberFlags;
  }
}

在 mountClassInstance 函数中,做了以下事情:

1、首先通过stateNode属性从workInProgress上获取class组件实例,并更新组件实例上的 state 和 props:

// 在 adoptClassInstance 函数里,将 class组件实例 挂载到了 workInProgress 的 stateNode 属性上
// 因此这里可以通过 workInProgress 的 stateNode 属性获取 class组件实例
const instance = workInProgress.stateNode;

// 更新 props/state
instance.props = newProps;
instance.state = workInProgress.memoizedState;
instance.refs = emptyRefsObject;

2、接着对 updateQueue 进行初始化,将updateQueue挂在workInProgress上:

// 初始化更新队列
initializeUpdateQueue(workInProgress);

3、接下来初始化 context,如果开发中定义class组件时提供了 contextType,则初始化为定义class组件时提供的 context,否则就初始化为一个空的emptyContextObject。

  // ctor 即 workInProgress.type,也就是我们开发中定义class组件时的那个 类
  const contextType = ctor.contextType;
  // 初始化 context
  if (typeof contextType === 'object' && contextType !== null) {
    instance.context = readContext(contextType);
  } else if (disableLegacyContext) {
    instance.context = emptyContextObject;
  } else {
    const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
    instance.context = getMaskedContext(workInProgress, unmaskedContext);
  }

4、再接着执行processUpdateQueue方法,更新updateQueue,并更新 state。由于在 processUpdateQueue 里更新了 state,因此 class组件实例的 state 也需要更新。

// 执行更新 updateQueue
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
// 在 processUpdateQueue 里 更新了 state,所以 instance 的 state 也需要更新
instance.state = workInProgress.memoizedState;

5、然后判断开发代码中是否提供了 getDerivedStateFromProps 生命周期函数,如果有,则调用 applyDerivedStateFromProps 函数来执行 getDerivedStateFromProps 生命周期函数,并更新 state,并同时更新class组件实例的state

// getDerivedStateFromProps 是 React 新的生命周期函数
// https://zh-hans.reactjs.org/docs/react-component.html#static-getderivedstatefromprops
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
  // 执行 getDerivedStateFromProps 生命周期函数,并更新 state
  applyDerivedStateFromProps(
    workInProgress,
    ctor,
    getDerivedStateFromProps,
    newProps,
  );
  // 更新 instance 的 state
  instance.state = workInProgress.memoizedState;
}

6、再然后判断开发代码中是否提供了 componentWillMount 生命周期函数,如果有,则调用 callComponentWillMount 函数来执行 componentWillMount 生命周期函数,并更新 state,同时也更新class组件实例上的 state。在 componentWillMount 生命周期函数中是有可能调用 setState 方法的,因此需要执行processUpdateQueue方法来更新updateQueue,并更新 state,由于在 processUpdateQueue 里更新了 state,因此 class组件实例的 state 也需要更新。

// 判断是否要调用 componentWillMount
// 如果是第一次渲染,是要执行 componentWillMount 的
if (
  typeof ctor.getDerivedStateFromProps !== 'function' &&
  typeof instance.getSnapshotBeforeUpdate !== 'function' &&
  (typeof instance.UNSAFE_componentWillMount === 'function' ||
    typeof instance.componentWillMount === 'function')
) {
  // 执行 componentWillMount 生命周期函数
  callComponentWillMount(workInProgress, instance);
  // If we had additional state updates during this life-cycle, let's
  // process them now.

  // 在 componentWillMount 钩子函数中是有可能执行 setState 的,
  // 所以要执行更新 updateQueue,并更新 state
  processUpdateQueue(workInProgress, newProps, instance, renderLanes);

  // 在 processUpdateQueue 中更新了 state,因此 instance 的 state 也要更新
  instance.state = workInProgress.memoizedState;
}

7、最后给 workInProgress 添加类型为 Update 的flags标记,在等到组件真正渲染到 DOM 上的时候,执行 componentDidMount 生命周期函数完成组件的挂载。

// 添加标记,等到组件真正渲染到 DOM 上的时候,再去调用 componentDidMount 钩子函数
if (typeof instance.componentDidMount === 'function') {
  let fiberFlags: Flags = Update;
  if (enableSuspenseLayoutEffectSemantics) {
    fiberFlags |= LayoutStatic;
  }

  // ...

  workInProgress.flags |= fiberFlags;
}

接下来,我们来逐个看看在 mountClassInstance 中调用的函数。

initializeUpdateQueue

initializeUpdateQueue 用于初始化 updateQueue

// react-reconciler/src/ReactUpdateQueue.new.js

export function initializeUpdateQueue<State>(fiber: Fiber): void {
  const queue: UpdateQueue<State> = {
    baseState: fiber.memoizedState,
    firstBaseUpdate: null,
    lastBaseUpdate: null,
    shared: {
      pending: null,
      interleaved: null,
      lanes: NoLanes,
    },
    effects: null,
  };
  fiber.updateQueue = queue;
}

可以看到,initializeUpdateQueue 函数定义了一个 queue 对象,并将其赋值给了当前工作节点 fiber 的 updateQueue 属性,也就是初始化当前工作节点 workInProgress 的 updateQueue。

processUpdateQueue

processUpdateQueue用于更新 updateQueue 队列,并更新 state

// react-reconciler/src/ReactUpdateQueue.new.js

export function processUpdateQueue<State>(
    workInProgress: Fiber,
    props: any,
    instance: any,
    renderLanes: Lanes,
  ): void {
    // This is always non-null on a ClassComponent or HostRoot
    // 在 initializeUpdateQueue函数中 初始化更新队列时将 updateQueue 挂载到了 workInProgress 上
    // 因此这里可以通过 workInProgress 获取更新队列
    const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
  
    // 是否要强制更新,在这里不需要强制更新
    hasForceUpdate = false;
  
    if (__DEV__) {
      currentlyProcessingQueue = queue.shared;
    }
  
    // 更新队列中的第一个 update
    let firstBaseUpdate = queue.firstBaseUpdate;
    //  更新队列中的最后一个 update
    let lastBaseUpdate = queue.lastBaseUpdate;
  
    // Check if there are pending updates. If so, transfer them to the base queue.
    // 检查更新队列中是否有待处理的更新,如果有,将它们添加到基本队列中
    let pendingQueue = queue.shared.pending;
    if (pendingQueue !== null) {
      queue.shared.pending = null;
  
      // The pending queue is circular. Disconnect the pointer between first
      // and last so that it's non-circular.
      const lastPendingUpdate = pendingQueue;
      const firstPendingUpdate = lastPendingUpdate.next;
      lastPendingUpdate.next = null;
      // Append pending updates to base queue
      // 将待处理的更新添加到基础队列中
      if (lastBaseUpdate === null) {
        // 将待更新的第一个 update 对象赋值给更新队列中的第一个更新元素
        firstBaseUpdate = firstPendingUpdate;
      } else {
        // 更新队列中存在要更新的元素,将待更新队列中的第一个更新元素添加到更新队列的末尾,形成链表
        lastBaseUpdate.next = firstPendingUpdate;
      }
      lastBaseUpdate = lastPendingUpdate;
  
      // If there's a current queue, and it's different from the base queue, then
      // we need to transfer the updates to that queue, too. Because the base
      // queue is a singly-linked list with no cycles, we can append to both
      // lists and take advantage of structural sharing.
      // TODO: Pass `current` as argument
      
      // 更新当前渲染在界面上的fiber树上的 updateQueue
      const current = workInProgress.alternate;
      if (current !== null) {
        // This is always non-null on a ClassComponent or HostRoot
        const currentQueue: UpdateQueue<State> = (current.updateQueue: any);
        const currentLastBaseUpdate = currentQueue.lastBaseUpdate;
        if (currentLastBaseUpdate !== lastBaseUpdate) {
          if (currentLastBaseUpdate === null) {
            currentQueue.firstBaseUpdate = firstPendingUpdate;
          } else {
            currentLastBaseUpdate.next = firstPendingUpdate;
          }
          currentQueue.lastBaseUpdate = lastPendingUpdate;
        }
      }
    }
  
    // These values may change as we process the queue.
    // 当执行更新队列的时候,这些属性可能会被更改
    if (firstBaseUpdate !== null) {
      // Iterate through the list of updates to compute the result.
      // 创建副本变量
      let newState = queue.baseState;
      // TODO: Don't need to accumulate this. Instead, we can remove renderLanes
      // from the original lanes.
      let newLanes = NoLanes;
  
      let newBaseState = null;
      let newFirstBaseUpdate = null;
      let newLastBaseUpdate = null;
  
      let update = firstBaseUpdate;
      do {
        // 获取 update 的优先级,判断是否需要执行更新
        const updateLane = update.lane;
        const updateEventTime = update.eventTime;
        // 更新队列的第一个 update 的优先级低于 renderLanes
        if (!isSubsetOfLanes(renderLanes, updateLane)) {
          // Priority is insufficient. Skip this update. If this is the first
          // skipped update, the previous update/state is the new base
          // update/state.
  
          // 由于优先级低于 renderLanes,不执行该 update 的更新
          const clone: Update<State> = {
            eventTime: updateEventTime,
            lane: updateLane,
  
            tag: update.tag,
            payload: update.payload,
            callback: update.callback,
  
            next: null,
          };
          // 本次没有更新的 update,会优先放到下一次去判断要不要更新
          if (newLastBaseUpdate === null) {
            newFirstBaseUpdate = newLastBaseUpdate = clone;
            newBaseState = newState;
          } else {
            newLastBaseUpdate = newLastBaseUpdate.next = clone;
          }
          // Update the remaining priority in the queue.
          newLanes = mergeLanes(newLanes, updateLane);
        } else {
          // This update does have sufficient priority.
  
          // 该 update 优先级高于 renderLanes,执行更新
          if (newLastBaseUpdate !== null) {
            const clone: Update<State> = {
              eventTime: updateEventTime,
              // This update is going to be committed so we never want uncommit
              // it. Using NoLane works because 0 is a subset of all bitmasks, so
              // this will never be skipped by the check above.
              lane: NoLane,
  
              tag: update.tag,
              payload: update.payload,
              callback: update.callback,
  
              next: null,
            };
            // 拷贝一份 update 
            newLastBaseUpdate = newLastBaseUpdate.next = clone;
          }
  
          // Process this update.
          // 执行 update,获取最新的 state
          newState = getStateFromUpdate(
            workInProgress,
            queue,
            update,
            newState,
            props,
            instance,
          );
          // callback 为 this.setState({}, () => {}) 中的回调函数 () => {}
          const callback = update.callback;
  
          // setState 执行完后,执行 callback
          if (
            callback !== null &&
            // If the update was already committed, we should not queue its
            // callback again.
            update.lane !== NoLane
          ) {
            workInProgress.flags |= Callback;
            const effects = queue.effects;
            if (effects === null) {
              queue.effects = [update];
            } else {
              effects.push(update);
            }
          }
        }
  
        // 执行下一个更新,循环
        update = update.next;
        if (update === null) {
          pendingQueue = queue.shared.pending;
          if (pendingQueue === null) {
            break;
          } else {
            // An update was scheduled from inside a reducer. Add the new
            // pending updates to the end of the list and keep processing.
            const lastPendingUpdate = pendingQueue;
            // Intentionally unsound. Pending updates form a circular list, but we
            // unravel them when transferring them to the base queue.
            const firstPendingUpdate = ((lastPendingUpdate.next: any): Update<State>);
            lastPendingUpdate.next = null;
            update = firstPendingUpdate;
            queue.lastBaseUpdate = lastPendingUpdate;
            queue.shared.pending = null;
          }
        }
      } while (true);
  
      // 执行 update 后,更新 queue 上的相关属性
      if (newLastBaseUpdate === null) {
        newBaseState = newState;
      }
  
      queue.baseState = ((newBaseState: any): State);
      queue.firstBaseUpdate = newFirstBaseUpdate;
      queue.lastBaseUpdate = newLastBaseUpdate;
  
      // Interleaved updates are stored on a separate queue. We aren't going to
      // process them during this render, but we do need to track which lanes
      // are remaining.
      const lastInterleaved = queue.shared.interleaved;
      if (lastInterleaved !== null) {
        let interleaved = lastInterleaved;
        do {
          newLanes = mergeLanes(newLanes, interleaved.lane);
          interleaved = ((interleaved: any).next: Update<State>);
        } while (interleaved !== lastInterleaved);
      } else if (firstBaseUpdate === null) {
        // `queue.lanes` is used for entangling transitions. We can set it back to
        // zero once the queue is empty.
        queue.shared.lanes = NoLanes;
      }
  
      // Set the remaining expiration time to be whatever is remaining in the queue.
      // This should be fine because the only two other things that contribute to
      // expiration time are props and context. We're already in the middle of the
      // begin phase by the time we start processing the queue, so we've already
      // dealt with the props. Context in components that specify
      // shouldComponentUpdate is tricky; but we'll have to account for
      // that regardless.
      markSkippedUpdateLanes(newLanes);
      workInProgress.lanes = newLanes;
      workInProgress.memoizedState = newState;
    }
  
    if (__DEV__) {
      currentlyProcessingQueue = null;
    }
}
  

在 processUpdateQueue 函数中:

1、从workInProgress上获取updateQueue,取出updateQueue中的第一个update,然后检查updateQueue中是否有待处理的更新,如果有,就将它们添加到基本队列中,并同时更新当前渲染到屏幕上的fiber树current的updateQueue。

  // 因此这里可以通过 workInProgress 获取更新队列
  const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);

  // 是否要强制更新,在这里不需要强制更新
  hasForceUpdate = false;

  // ...

  // 更新队列中的第一个 update
  let firstBaseUpdate = queue.firstBaseUpdate;
  //  更新队列中的最后一个 update
  let lastBaseUpdate = queue.lastBaseUpdate;

  // Check if there are pending updates. If so, transfer them to the base queue.
  // 检查更新队列中是否有待处理的更新,如果有,将它们添加到基本队列中
  let pendingQueue = queue.shared.pending;
  if (pendingQueue !== null) {
    queue.shared.pending = null;

    // The pending queue is circular. Disconnect the pointer between first
    // and last so that it's non-circular.
    const lastPendingUpdate = pendingQueue;
    const firstPendingUpdate = lastPendingUpdate.next;
    lastPendingUpdate.next = null;
    // Append pending updates to base queue
    // 将待处理的更新添加到基础队列中
    if (lastBaseUpdate === null) {
      // 将待更新的第一个 update 对象赋值给更新队列中的第一个更新元素
      firstBaseUpdate = firstPendingUpdate;
    } else {
      // 更新队列中存在要更新的元素,将待更新队列中的第一个更新元素添加到更新队列的末尾,形成链表
      lastBaseUpdate.next = firstPendingUpdate;
    }
    lastBaseUpdate = lastPendingUpdate;

    
    // 更新当前渲染在界面上的fiber树上的 updateQueue
    const current = workInProgress.alternate;
    if (current !== null) {
      // This is always non-null on a ClassComponent or HostRoot
      const currentQueue: UpdateQueue<State> = (current.updateQueue: any);
      const currentLastBaseUpdate = currentQueue.lastBaseUpdate;
      if (currentLastBaseUpdate !== lastBaseUpdate) {
        if (currentLastBaseUpdate === null) {
          currentQueue.firstBaseUpdate = firstPendingUpdate;
        } else {
          currentLastBaseUpdate.next = firstPendingUpdate;
        }
        currentQueue.lastBaseUpdate = lastPendingUpdate;
      }
    }
  }

2、进入update的循环更新,并根据当前update的优先级,判断是否对当前update执行更新。

3、如果当前update的优先级低于 renderLanes,则不执行当前update的更新。拷贝当前update,将其重新放入updateQueue中,并更新它的更新优先级,等待下一次判断是否需要执行更新。

// 更新队列的第一个 update 的优先级低于 renderLanes
if (!isSubsetOfLanes(renderLanes, updateLane)) {
  // Priority is insufficient. Skip this update. If this is the first
  // skipped update, the previous update/state is the new base
  // update/state.

  // 由于优先级低于 renderLanes,不执行该 update 的更新
  const clone: Update<State> = {
    eventTime: updateEventTime,
    lane: updateLane,

    tag: update.tag,
    payload: update.payload,
    callback: update.callback,

    next: null,
  };
  // 本次没有更新的 update,会优先放到下一次去判断要不要更新
  if (newLastBaseUpdate === null) {
    newFirstBaseUpdate = newLastBaseUpdate = clone;
    newBaseState = newState;
  } else {
    newLastBaseUpdate = newLastBaseUpdate.next = clone;
  }
  // Update the remaining priority in the queue.
  newLanes = mergeLanes(newLanes, updateLane);
}

4、如果当前update的优先级高于 renderLanes,则执行当前update的更新,执行 getStateFromUpdate() 获取更新的 state 后,如果该update上有callback的话,即this.setState({}, () => {}) 中的回调函数,设置 workInProgress 的 flags 为 Callback 来“提醒”更新 state 后再执行 callback。

else {
  // This update does have sufficient priority.

  // 该 update 优先级高于 renderLanes,执行更新
  if (newLastBaseUpdate !== null) {
    const clone: Update<State> = {
      eventTime: updateEventTime,
      // This update is going to be committed so we never want uncommit
      // it. Using NoLane works because 0 is a subset of all bitmasks, so
      // this will never be skipped by the check above.
      lane: NoLane,

      tag: update.tag,
      payload: update.payload,
      callback: update.callback,

      next: null,
    };
    // 拷贝一份 update 
    newLastBaseUpdate = newLastBaseUpdate.next = clone;
  }

  // Process this update.
  // 执行 update,获取最新的 state
  newState = getStateFromUpdate(
    workInProgress,
    queue,
    update,
    newState,
    props,
    instance,
  );
  // callback 为 this.setState({}, () => {}) 中的回调函数 () => {}
  const callback = update.callback;

  // setState 执行完后,执行 callback
  if (
    callback !== null &&
    // If the update was already committed, we should not queue its
    // callback again.
    update.lane !== NoLane
  ) {
    workInProgress.flags |= Callback;
    const effects = queue.effects;
    if (effects === null) {
      queue.effects = [update];
    } else {
      effects.push(update);
    }
  }
}

5、第一个 update 执行完更新后,通过 update = update.next 获取到下一个 update,重复 2、3、4,继续执行下一个update的更新。

// 执行下一个更新,循环
update = update.next;

6、当updateQueue中的所有update都执行完更新后,更新updateQueue和workInProgress上的一些属性。

// 执行 update 后,更新 queue 上的相关属性
if (newLastBaseUpdate === null) {
  newBaseState = newState;
}

queue.baseState = ((newBaseState: any): State);
queue.firstBaseUpdate = newFirstBaseUpdate;
queue.lastBaseUpdate = newLastBaseUpdate;

const lastInterleaved = queue.shared.interleaved;
if (lastInterleaved !== null) {
  let interleaved = lastInterleaved;
  do {
    newLanes = mergeLanes(newLanes, interleaved.lane);
    interleaved = ((interleaved: any).next: Update<State>);
  } while (interleaved !== lastInterleaved);
} else if (firstBaseUpdate === null) {
  queue.shared.lanes = NoLanes;
}

markSkippedUpdateLanes(newLanes);
workInProgress.lanes = newLanes;
workInProgress.memoizedState = newState;

getStateFromUpdate

getStateFromUpdate 函数用于获取最新是state

// react-reconciler/src/ReactUpdateQueue.new.js

function getStateFromUpdate<State>(
  workInProgress: Fiber,
  queue: UpdateQueue<State>,
  update: Update<State>,
  prevState: State,
  nextProps: any,
  instance: any,
): any {
  switch (update.tag) {

    // 通过 replaceState()的更新  即替换更新
    // 返回 执行 payload 后的 state
    case ReplaceState: {
      const payload = update.payload;
      if (typeof payload === 'function') {
        // Updater function

        // 此处删除了 dev 代码

        const nextState = payload.call(instance, prevState, nextProps);

        // 此处删除了 dev 代码

        return nextState;
      }
      // State object
      return payload;
    }

    // 捕获性更新
    case CaptureUpdate: {
      workInProgress.flags =
        // ~ 的意思是获取除了 ShouldCapture 以外的所有属性,也就是获取了 DidCapture
        (workInProgress.flags & ~ShouldCapture) | DidCapture;
    }

    // Intentional fallthrough
    // 通过 setState 更新
    case UpdateState: {
      const payload = update.payload;
      let partialState;
      // 如果payload 是 function,则获取执行 payload 后得到的 state
      if (typeof payload === 'function') {
        // Updater function

        // 此处删除了 dev 代码

        partialState = payload.call(instance, prevState, nextProps);

        // 此处删除了 dev 代码

      } else {
        // Partial state object
        // payload 不是 function,直接将其赋值给 partialState
        partialState = payload;
      }
      // partialState 没有值,则视为没有更新 state,直接返回 prevState
      if (partialState === null || partialState === undefined) {
        // Null and undefined are treated as no-ops.
        return prevState;
      }
      // Merge the partial state and the previous state.
      // 合并 state,并返回合并后的 state
      return Object.assign({}, prevState, partialState);
    }

    // 强制更新
    case ForceUpdate: {
      hasForceUpdate = true;
      return prevState;
    }
  }
  return prevState;
}

getStateFromUpdate 对替换更新(ReplaceState)、捕获性更新(CaptureUpdate)、setState更新(UpdateState)、强制更新(ForceUpdate) 四种更新类型做了不同的处理。我们重点来看看 UpdateState 的情况,也就是我们在开发的时候使用this.setState({xxx})的情况,如果有新 state 的话,则执行Object.assign({}, prevState, partialState)来进行 state 的合并,然后返回合并后的 state 。

applyDerivedStateFromProps

applyDerivedStateFromProps 的作用是 执行 getDerivedStateFromProps 生命周期函数,并更新 state

// react-reconciler/src/ReactFiberClassComponent.new.js

function applyDerivedStateFromProps(
  workInProgress: Fiber,
  ctor: any,
  getDerivedStateFromProps: (props: any, state: any) => any,
  nextProps: any,
) {
  const prevState = workInProgress.memoizedState;
  // 执行 getDerivedStateFromProps 生命周期函数
  let partialState = getDerivedStateFromProps(nextProps, prevState);
  
  // ...
    
  // Merge the partial state and the previous state.
  // 合并新的state和旧的state
  const memoizedState =
    partialState === null || partialState === undefined
      ? prevState
      : Object.assign({}, prevState, partialState);
  // 更新state   
  workInProgress.memoizedState = memoizedState;

  // Once the update queue is empty, persist the derived state onto the
  // base state.
  // 更新 updateQueue上的 state
  if (workInProgress.lanes === NoLanes) {
    // Queue is always non-null for classes
    const updateQueue: UpdateQueue<any> = (workInProgress.updateQueue: any);
    updateQueue.baseState = memoizedState;
  }
}

可以看到,在 applyDerivedStateFromProps 函数中, 执行 getDerivedStateFromProps 生命周期函数获取新的 state,然后将新的state和旧的state进行合并,将合并后的state更新到workInProgress上和updateQueue上。

callComponentWillMount

callComponentWillMount的作用就是,执行 componentWillMount 生命周期函数

// react-reconciler/src/ReactFiberClassComponent.new.js

function callComponentWillMount(workInProgress, instance) {
  const oldState = instance.state;

  // 执行 componentWillMount 生命周期函数   
  if (typeof instance.componentWillMount === 'function') {
    instance.componentWillMount();
  }
  // 执行 UNSAFE_componentWillMount 生命周期函数   
  if (typeof instance.UNSAFE_componentWillMount === 'function') {
    instance.UNSAFE_componentWillMount();
  }

  if (oldState !== instance.state) {
    if (__DEV__) {
      console.error(
        '%s.componentWillMount(): Assigning directly to this.state is ' +
        "deprecated (except inside a component's " +
        'constructor). Use setState instead.',
        getComponentNameFromFiber(workInProgress) || 'Component',
      );
    }
    classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
  }
}

可以看到,在 callComponentWillMount 函数中,分别执行了 componentWillMount 生命周期函数和 UNSAFE_componentWillMount 生命周期函数。如果是在开发环境,则执行 enqueueReplaceState 来替换更新组件实例上的 state 。

流程图

下面是class组件实例未被创建时,构建class组件实例并挂载组件及执行一些相关生命周期函数的流程图:

总结

本文是 React 源码解读之class组件更新updateClassComponent 系列的第二篇,介绍了ClassComponent的三种更新情形中的第一种。ClassComponent实例未被创建,此时会调用 constructClassInstance 方法构建class组件实例,然后调用 mountClassInstance 方法挂载class组件,并将 shouldUpdate 置为 true,标记组件需要更新渲染。

下一篇文章《React 源码解读之class组件更新updateClassComponent (三)》将介绍ClassComponent的三种更新情形中的第二种。欢迎前往阅读。