React16源码: React中commit阶段的commitBeforeMutationLifecycles的源码实现

commitBeforeMutationLifecycles


1 )概述

  • 在 react commit阶段的 commitRoot 第一个while循环中
  • 调用了 commitBeforeMutationLifeCycles
  • 现在来看下,里面发生了什么

2 )源码

回到 commit 阶段的第一个循环中,在 commitRoot 函数里
定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L647

看这里

// Invoke instances of getSnapshotBeforeUpdate before mutation.
nextEffect = firstEffect;
startCommitSnapshotEffectsTimer();

// 接下去就进入了三个循环:
// 第一个循环主要是 commitBeforeMutationLifecycles,这个方法它其实很简单,它是这三个循环当中最简单的一个方法
// 它唯一的作用就是调用 ClassComponent 上面可能会存在的 getSnapshotBeforeUpdate 这么一个生命周期方法
while (nextEffect !== null) {
  let didError = false;
  let error;
  if (__DEV__) {
    invokeGuardedCallback(null, commitBeforeMutationLifecycles, null);
    if (hasCaughtError()) {
      didError = true;
      error = clearCaughtError();
    }
  } else {
    try {
      commitBeforeMutationLifecycles();
    } catch (e) {
      didError = true;
      error = e;
    }
  }
  if (didError) {
    invariant(
      nextEffect !== null,
      'Should have next effect. This error is likely caused by a bug ' +
        'in React. Please file an issue.',
    );
    captureCommitPhaseError(nextEffect, error);
    // Clean-up
    if (nextEffect !== null) {
      nextEffect = nextEffect.nextEffect;
    }
  }
}
  • 在开始循环之前都会用到一个全局变量叫做 nextEffect,当然它一开始是 firstEffect
    • 是在 RootFiber 上面,它的 firstEffect 到 lastEffect 这个链表上面的第一个effect
    • 也就是第一个需要我们commit的fiber对象
  • 需要注意一点,这边有一个很奇怪的代码,就是我们这个 commitBeforeMutationLifecycles 方法
    • 可以看到 import { commitBeforeMutationLifeCycles } from './ReactFiberCommitWork'
    • 但是又在本文件中,定义了一个同名的方法
      // 这个是核心
      function commitBeforeMutationLifecycles() {
        while (nextEffect !== null) {
          if (__DEV__) {
            ReactCurrentFiber.setCurrentFiber(nextEffect);
          }
      
          const effectTag = nextEffect.effectTag;
          if (effectTag & Snapshot) {
            recordEffect();
            const current = nextEffect.alternate;
            commitBeforeMutationLifeCycles(current, nextEffect); // 这个函数,不是上层的自己,而是 import 进来的
          }
      
          nextEffect = nextEffect.nextEffect;
        }
      
        if (__DEV__) {
          ReactCurrentFiber.resetCurrentFiber();
        }
      }
      
  • 这个操作会有点奇怪,因为正常来讲,import 进来的内容,它是一个常量,它不应该是能够直接被修改
  • 注意,在 commitRoot 里调用的 commitBeforeMutationLifeCycles 是文件内的这个方法
  • 而文件内的这个方法,调用的 commitBeforeMutationLifeCycles 是 import 进来的
    • 它来自于 packages/react-reconciler/src/ReactFiberCommitWork.js#L215
  • 它通过一个while循环,循环的是我们的 effect链
    • 在这个循环的后面,可以看到 nextEffect = nextEffect.nextEffect;
  • 这就是说一个一个往下这么去循环,对 有 Snapshot 这个 SideEffect 的节点执行了下面的方法

再次定位到 packages/react-reconciler/src/ReactFiberCommitWork.js#L215

进入 commitBeforeMutationLifeCycles 这个就是上面定义的同名函数里面调用的方法

function commitBeforeMutationLifeCycles(
  current: Fiber | null,
  finishedWork: Fiber,
): void {
  switch (finishedWork.tag) {
    case FunctionComponent:
    case ForwardRef:
    case SimpleMemoComponent: {
      commitHookEffectList(UnmountSnapshot, NoHookEffect, finishedWork);
      return;
    }
    case ClassComponent: {
      if (finishedWork.effectTag & Snapshot) {
        if (current !== null) {
          const prevProps = current.memoizedProps;
          const prevState = current.memoizedState;
          startPhaseTimer(finishedWork, 'getSnapshotBeforeUpdate');
          const instance = finishedWork.stateNode;
          // We could update instance props and state here,
          // but instead we rely on them being set during last render.
          // TODO: revisit this when we implement resuming.
          if (__DEV__) {
            if (finishedWork.type === finishedWork.elementType) {
              warning(
                instance.props === finishedWork.memoizedProps,
                'Expected instance props to match memoized props before ' +
                  'getSnapshotBeforeUpdate. This is likely due to a bug in React. ' +
                  'Please file an issue.',
              );
              warning(
                instance.state === finishedWork.memoizedState,
                'Expected instance state to match memoized state before ' +
                  'getSnapshotBeforeUpdate. This is likely due to a bug in React. ' +
                  'Please file an issue.',
              );
            }
          }
          const snapshot = instance.getSnapshotBeforeUpdate(
            finishedWork.elementType === finishedWork.type
              ? prevProps
              : resolveDefaultProps(finishedWork.type, prevProps),
            prevState,
          );
          if (__DEV__) {
            const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<
              mixed,
            >);
            if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
              didWarnSet.add(finishedWork.type);
              warningWithoutStack(
                false,
                '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +
                  'must be returned. You have returned undefined.',
                getComponentName(finishedWork.type),
              );
            }
          }
          instance.__reactInternalSnapshotBeforeUpdate = snapshot;
          stopPhaseTimer();
        }
      }
      return;
    }
    case HostRoot:
    case HostComponent:
    case HostText:
    case HostPortal:
    case IncompleteClassComponent:
      // Nothing to do for these component types
      return;
    default: {
      invariant(
        false,
        'This unit of work tag should not have side-effects. This error is ' +
          'likely caused by a bug in React. Please file an issue.',
      );
    }
  }
}
  • 它传入的 current 是 workInProgress 对象,也就是fiber对象
  • 然后传入的 nextEfect 是 finishedWork 对象
  • 通过 instance.getSnapshotBeforeUpdate 传入两个 props 来获取 snapshot 快照对象
  • 之后将 snapshot 挂载到 instance.__reactInternalSnapshotBeforeUpdate
  • 对于其他类型的组件,它都没有任何的处理,只处理了 ClassComponent
  • 因为对这种情况来说,只有 ClassComponent 有这个 SideEffect
  • 并且会有 getSnapshotBeforeUpdate 这个生命周期方法的

你可能感兴趣的:(React,React,Native,react.js,前端,前端框架)