美文网首页React前端技术
React 源码探源 5 useEffect, useLayou

React 源码探源 5 useEffect, useLayou

作者: 吴摩西 | 来源:发表于2021-07-04 18:17 被阅读0次

    背景

    接上一章 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 (<div id="div">
        <button id="btn" onClick={() => {
          setCount(function add(c) {return c + 1;});
        }}>click me</button>
        <div>the new text is <span>{count}</span></div>
      </div>);
    }
    

    详细流程

    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, useLayou

        本文链接:https://www.haomeiwen.com/subject/znduultx.html