useIsMounted 的实现

2种实现方式对比,深入对 react 的了解
更新于: 2024-11-30 22:35:23

场景

isMounted 的实现,这个 hoook 重命名为 useStaticMounted/useMountedFlag/useMountTracker

import { useEffect, useRef } from 'react';

const useIsMounted = () => {
  const isMounted = useRef(false);

  useEffect(() => {
    isMounted.current = true;
    return () => {
      isMounted.current = false;
    };
  }, []);

  return isMounted.current;
};

export default useIsMounted;

使用 useState

import { useEffect, useState } from 'react';

const useIsMounted = () => {
  const [isMounted, setIsMounted] = useState(false);

  useEffect(() => {
    setIsMounted(true);
    return () => setIsMounted(false);
  }, []);

  return isMounted;
};

export default useIsMounted;

2种方式对比

在实现 `useIsMounted` 这个 Hook 时,直接使用 `useRef` 是更合适的选择,而不是使用 `useState`。原因如下:

### 1. **`useRef` vs `useState` 性能开销**
   - **`useRef`**: 它的 `.current` 属性在组件重新渲染时不会改变,因此不触发重新渲染。
   - **`useState`**: 状态更新会触发组件重新渲染。如果用 `useState` 实现 `isMounted`,状态的更新会导致额外的重新渲染,这是没有必要的。

### 2. **功能需求**
   - `useIsMounted` 的需求是追踪组件的挂载状态,而不是动态触发渲染。`useRef` 的设计正好适合这种“保存某个值而不参与渲染逻辑”的场景。
   - 如果用 `useState`,状态更新的触发会违背它的初衷,增加不必要的复杂性。

### `useState` 实现的对比示例

用 `useState` 实现可能像这样:

```javascript
import { useEffect, useState } from 'react';

const useIsMounted = () => {
  const [isMounted, setIsMounted] = useState(false);

  useEffect(() => {
    setIsMounted(true);
    return () => setIsMounted(false);
  }, []);

  return isMounted;
};

export default useIsMounted;
```

### 缺点
- 每次状态更新都会触发组件的重新渲染,即使 `isMounted` 的值没有在渲染中直接使用。
- 在一些性能敏感的场景下(如高频渲染组件),可能引入性能问题。

### 总结
你当前的实现基于 `useRef` 是更合理的。`useState` 适合管理需要触发重新渲染的值,而 `useRef` 则适合保存不会影响渲染的值。你的代码完全符合最佳实践,无需改用 `useState`。

更优的写法

写法来源于 https://github.com/juliencrn/usehooks-ts/blob/master/packages/usehooks-ts/src/useIsMounted/useIsMounted.ts

export function useIsMounted(): () => boolean {
  const isMounted = useRef(false)

  useEffect(() => {
    isMounted.current = true

    return () => {
      isMounted.current = false
    }
  }, [])

  return useCallback(() => isMounted.current, [])
}