React 源码探源 5 useEffect, useLayoutEffect

背景

接上一章 React 源码探源 4 useState,来研究一下 useEffect 与 useLayoutEffect 相关的实现细节。

相关定义

先来看一下 React 相关的官方文档

  1. useEffect,
    1. 作用:此 hook 主要是用来在组件渲染完成以后执行一些称为 effect 的操作,例如发送 ajax 请求,打点,当一些 state 发生变化以后,再更新其它的 state 等。
    2. 执行时机:useEffect 实在下次渲染之前执行,执行时浏览器已经对上次状态更新渲染完成。
    3. 返回值:useEffect可以返回一个回调函数,当组件 unMount 时,会被调用。
  2. useLayoutEffect
    1. 作用:如文档所示,绝大部分情况下都推荐使用 useEffect,只有使用 useEffect 的结果有些怪异时才会使用这个 hook。 笔者根据实验发现,只有更改 DOM 时导致了一些抖动的行为时使用 useLayoutEffect 时才会派上用场。
    2. 执行时机:useLayoutEffect 执行时,浏览器还未对 DOM 进行渲染。可以获取新的 DOM 进行操作。执行的时机较 useEffect 更早。
    3. 返回值:useLayoutEffect 也可以返回一个回调函数,也会在 unMount 时被调用。调用的时机也会较 useEffect 的回调更早。

示例代码

本次实例使用的详细代码如下

function Dev() {
  const [count, setCount] = React.useState(0);
  React.useEffect(function effectCb() {
    console.log('in effect');
    if (count === 1) {
      setCount(10);
    }
    return function effectUnMount() {
      console.log('effect unmount');
    };
  }, [count]);
  React.useLayoutEffect(function layoutEffectCb() {
    console.log('in layout effect', count);
    return function layoutEffectUnMount() {
      console.log('layout effect unmount');
    };
  }, [count]);
  return (
the new text is {count}
); }

详细流程

useEffect 和 useLayoutEffect 的详细流程

render

  • 在执行 useEffect, useLayoutEffect 时会将对应的回调存储起来,详细结构参加下部分
  • 更新时,会检查对应的 dependency 是不是有变化再决定是否将 effect 加进来。

commit

  • useEffect 的列表会先被检查,如果有更新,会使用MessageChannel.postMessage 计划在下次 eventLoop 执行。从代码注释中看到,这样的执行方式比 setTimeout 要好,因为 setTimeout 至少有 4ms 的延迟。
  • useLayoutEffect 会在commitMutationEffects,也就是内存中的 DOM 更新以后马上执行,执行的时机比 useEffect 更早。
  • effect 执行时,会检查 fiber 的 updateQueue 中是否含有对应类型的 effect,并将它顺序执行。
  • 执行过程类似与 useState 的调用类似。
    • 通过 subtreeFlags 来检查子节点是否有 effect 需要执行。
    • 通过 flags 检查当前节点有 effect 需要执行。

effect 数据结构

可以看到以下的信息:

  1. useLayoutEffectuseEffect 生成的 hook 会跟useState生成的 hook 一起存储在 fiber 的 memorizedState 下面的链表里面。
  2. fiber 的 updateQueue 使用 lastEffect 存储着所有的 effect 生成的循环链表。
  3. hook 中的 memorizedStatelastEffect指向相同的地方,存储着 effect 的相关信息
    • create: effect 的回调函数
    • tag: 存储着 effect 的类型 Passive = 4标记着 useEffect LayoutStatic = 2 标记着 useLayoutEffect
    • destroy: effect 返回的回调函数
    • next: 下一个可能的 effect

unMount 过程

在组件卸载时,执行的顺序和机制与加载和更新时一致,只是在检查到 fiber 被删除时进行操作。

你可能感兴趣的:(React 源码探源 5 useEffect, useLayoutEffect)