laitimes

Why do you need useMount and useUnmount?

author:Web front-end development

background

When using React class components, React has an obvious lifecycle method, and you can do the logic in the determined lifecycle method, since the launch of React Hooks, React has not introduced a clear lifecycle method, but more uses useEffect to simulate lifecycle methods, such as useMount, useUnmount, etc.

However, after using React Hooks in my project for a long time, I found that using useEffect to simulate the life cycle method would lead to some new problems

React Hooks 的执行顺序

Here's a diagram of the execution sequence of React Hooks from the community

Why do you need useMount and useUnmount?

Principle analysis

Explain the order in which React Hooks are executed with a simple piece of code

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;

           

Run the code for the first time in the browser and get the following result

Why do you need useMount and useUnmount?

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

Click the button and get a new result as follows

Why do you need useMount and useUnmount?

通过结果可以得到,先执行 useMemo,接着执行 useLayoutEffect 的 side effect cleanup,useEffect 的 side effect cleanup,等到 React update DOM and Refs 执行后,再执行 useLayoutEffect、useEffect。 通过这个例子,基本上弄清楚 useEffect 的执行顺序,由此分析使用 useEffect 模拟生命周期方法不当会导致什么问题

  • For the lifecycle mount method, use useEffect to simulate mount as follows
useEffect(() => {
  // do something
}, []);
           

The usage is equivalent to the mount method only if the deps parameter of useEffect is an empty array

  • For the lifecycle method of component destruction, use useEffect to simulate unmount as follows
useEffect(() => {
  return () => {
    // do something
  }
}, []);
           

useMount 和 useUnmount

The root cause of the previous problem is that useEffect needs to be decoupled from Lifecycle Methods. If the deps parameter of the useEffect is not an empty array, then the current useEffect is not equivalent to the mount method and will cause unexpected results

There are dedicated ahooks in the community to solve this problem, and there are two important methods in this third-party library, which are useMount and useUnmount

The implementation of useMount is as follows

// 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

           

The implementation of useUnmount is as follows

// 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'
}
           

Use useMount and useUnMount in your project like this

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

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

  return <></>
}
           

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