当前位置: 首页 > news >正文

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-upif (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 typesreturn;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 这个生命周期方法的
http://www.lryc.cn/news/290308.html

相关文章:

  • 压制二元组的总价值
  • 【习题】保存应用数据
  • Flask框架小程序后端分离开发学习笔记《5》简易服务器代码
  • “计算机视觉处理设计开发工程师”专项培训(第二期)
  • R语言学习case7:ggplot基础画图(核密度图)
  • Ubuntu18配置Docker
  • Keil/MDK平台 - 结构体成员指针注意事项
  • 一款超级好用的远程控制APP,你值得拥有
  • NumPy必知必会50例 | 18. 使用 NumPy 解决线性方程组:数学问题的实用解决方案
  • C/C++编码问题研究
  • 二刷代码随想录|Java版|回溯算法3|子集问题
  • mongodb config
  • pytorch 实现中文文本分类
  • 【MySQL】聚合函数和内置函数
  • python第五节:集合set(4)
  • 知识笔记(一百)———什么是okhttp?
  • Electron桌面应用实战:Element UI 导航栏橙色轮廓之谜与Bootstrap样式冲突解决方案
  • Nuget包缓存存放位置迁移
  • 键盘上Ins键的作用
  • css display 左右对齐 技巧
  • 【Linux操作系统】:Linux开发工具编辑器vim
  • Good Trip Codeforces Round 921 (Div. 2) 1925D
  • 推荐一款Linux、数据库、Redis、MongoDB统一管理平台!
  • TensorFlow2实战-系列教程6:迁移学习实战
  • 怎样开发adobe indesign插件,具体流程?
  • Docker 安装与基本操作
  • 译文带你理解Python的dataclass装饰器
  • 【C语言】实现程序的暂停
  • Hana SQL+正则表达式
  • 【笔记】顺利通过EMC试验(16-41)-视频笔记