解密使用useMount和useUnmount的重要性

目录
文章目录隐藏
  1. React Hooks 的执行顺序
  2. 原理分析
  3. useMount 和 useUnmount

解密 useMount 和 useUnmount 的重要性

在使用 React 类组件时,React 提供了明显的生命周期方法,可以在特定的生命周期方法中处理特定的逻辑。然而,自从 React Hooks 推出以来,React 并没有明确推出相应的生命周期方法,而是更多地使用useEffect来模拟生命周期方法,例如useMountuseUnmount等等。

然而,在笔者长期使用 React Hooks 进行项目开发的过程中,发现不正确地使用useEffect来模拟生命周期方法可能会引发一些新的问题。

React Hooks 的执行顺序

下面是一份来自社区的 React Hooks 的执行顺序图:

React Hooks 的执行顺序图

原理分析

以一段简单的代码解释 React Hooks 的执行顺序:

import { useEffect, useMemo, useState, useLayoutEffect } from "react";

const Count = () => {
  const [count, setCount] = useState(() => {
    console.log(1);
    return 0;
  });

  const double = useMemo(() => {
    console.log(2);
    return count * 2;
  }, [count]);

  const handleClick = () => setCount((c) => c + 1);

  useEffect(() => {
    console.log(4);
    return () => {
      console.log(6);
    };
  }, [count]);

  useLayoutEffect(() => {
    console.log(3);

    return () => {
      console.log(5);
    };
  }, [count]);

  return (
    <div>
      <p>
        {count}---{double}
      </p>
      <button onClick={handleClick}>click</button>
    </div>
  );
};

export default Count;

在浏览器中首次运行该代码,结果如下:

代码解释 React Hooks 的执行顺序

React update DOM and Refs 作为执行顺序的分界线,会先执行 useStateuseMemouseLayoutEffect 以及内部的变量或方法声明,后执行 useEffect

点击 click 按钮,得到了一份新结果如下:

React update DOM and Refs 作为执行顺序

通过结果可以得到,先执行 useMemo,接着执行 useLayoutEffect 的 side effect cleanup,useEffect 的 side effect cleanup,等到 React update DOM and Refs 执行后,再执行 useLayoutEffectuseEffect

通过这个例子,基本上弄清楚 useEffect 的执行顺序,由此分析使用 useEffect 模拟生命周期方法不当会导致什么问题。

  • 对于生命周期挂载方法,使用 useEffect 模拟 mount 如下:
    useEffect(() => {
      // do something
    }, []);
    

    只有当useEffect的 deps 参数是空数组时,该用法才等同于mount方法

  • 对于组件销毁的生命周期方法,使用useEffect模拟unmount如下:
    useEffect(() => {
      return () => {
        // do something
      }
    }, []);
    

useMount 和 useUnmount

前面的问题根本原因是 useEffect 与 Lifecycle Methods 需要解耦。如果 useEffectdeps 参数不是空数组,那么当前的 useEffect 不等同 mount 方法,就会造成意外的结果

社区里提供专门的 ahooks 解决这个问题,这个第三方库中有两个重要方法,分别是 useMount 和 useUnmount

useMount 的实现如下:

// useMount.ts

import { useEffect } from 'react'

const useMount = (fn: () => void) => {
  if (!isFunction(fn)) {
    console.error(`useMount: parameter \`fn\` expected to be a function, but got "${typeof fn}".`)
  }

  useEffect(() => {
    fn?.()
  }, [])
}

export default useMount

useUnmount 的实现如下:

// useUnMount.ts

import { useEffect, useRef } from 'react'

export default function useUnmount(fn: () => void): void {
  if (!isFunction(fn)) {
   console.error(`useUnmount: parameter \`fn\` expected to be a function, but got "${typeof fn}".`)
  }

  const ref = useRef(fn)

  useEffect(
    (): (() => void) => (): void => {
      ref.current?.()
    },
    []
  )
}

function isFunction(fn: unknown): fn is Function {
  return typeof fn === 'function'
}

在项目中这样使用useMountuseUnMount

const App = () => {
  useMount(() => {
    // ...
  })

  useUnMount(() => {
    // ...
  })

  return <></>
}

使用useMountuseUnmount就不用考虑传入的依赖,实现useEffect与 Lifecycle Methods 解耦

以上就是关于我们为什么需要 useMount 和 useUnmount 的详细内容,更多请关注码云笔记其它相关文章!

「点点赞赏,手留余香」

1

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系maynote@foxmail.com处理
码云笔记 » 解密使用useMount和useUnmount的重要性

发表回复