解密使用useMount和useUnmount的重要性
在使用 React 类组件时,React 提供了明显的生命周期方法,可以在特定的生命周期方法中处理特定的逻辑。然而,自从 React Hooks 推出以来,React 并没有明确推出相应的生命周期方法,而是更多地使用useEffect
来模拟生命周期方法,例如useMount
、useUnmount
等等。
然而,在笔者长期使用 React Hooks 进行项目开发的过程中,发现不正确地使用useEffect
来模拟生命周期方法可能会引发一些新的问题。
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 update DOM and Refs 作为执行顺序的分界线,会先执行 useState
、useMemo
、 useLayoutEffect
以及内部的变量或方法声明,后执行 useEffect
。
点击 click 按钮,得到了一份新结果如下:
通过结果可以得到,先执行 useMemo
,接着执行 useLayoutEffect
的 side effect cleanup,useEffect
的 side effect cleanup,等到 React update DOM and Refs 执行后,再执行 useLayoutEffect
、useEffect
。
通过这个例子,基本上弄清楚 useEffect 的执行顺序,由此分析使用 useEffect
模拟生命周期方法不当会导致什么问题。
- 对于生命周期挂载方法,使用
useEffect
模拟mount
如下:useEffect(() => { // do something }, []);
只有当
useEffect
的 deps 参数是空数组时,该用法才等同于mount
方法 - 对于组件销毁的生命周期方法,使用
useEffect
模拟unmount
如下:useEffect(() => { return () => { // do something } }, []);
useMount 和 useUnmount
前面的问题根本原因是 useEffect 与 Lifecycle Methods 需要解耦。如果 useEffect
的 deps
参数不是空数组,那么当前的 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' }
在项目中这样使用useMount
和useUnMount
:
const App = () => { useMount(() => { // ... }) useUnMount(() => { // ... }) return <></> }
使用useMount
和useUnmount
就不用考虑传入的依赖,实现useEffect
与 Lifecycle Methods 解耦
以上就是关于我们为什么需要 useMount 和 useUnmount 的详细内容,更多请关注码云笔记其它相关文章!
码云笔记 » 解密使用useMount和useUnmount的重要性