如何在 React 中正确处理组件卸载后异步操作的状态更新以避免内存泄漏?

如何在 React 中正确处理组件卸载后异步操作的状态更新以避免内存泄漏?

在 React 中,当组件卸载后如果仍有异步操作(如 fetch 请求、setTimeout 等)完成并尝试调用 setState,会导致 React 报警告:“Can't perform a React state update on an unmounted component.” 这不仅影响性能,还可能引发内存泄漏。

解决方法通常是在组件卸载时取消或忽略这些异步操作。常见做法包括:

  1. 使用 useRef 标记组件挂载状态
import { useEffect, useState, useRef } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);
  const isMounted = useRef(true);

  useEffect(() => {
    const fetchData = async () => {
      const result = await fetch('/api/data');
      const json = await result.json();
      if (isMounted.current) {
        setData(json);
      }
    };

    fetchData();

    return () => {
      isMounted.current = false; // 组件卸载时标记为 false
    };
  }, []);

  return <div>{data ? data.message : 'Loading...'}</div>;
}
  1. 使用 AbortController(适用于 fetch)
useEffect(() => {
  const controller = new AbortController();

  const fetchData = async () => {
    try {
      const response = await fetch('/api/data', {
        signal: controller.signal
      });
      const json = await response.json();
      setData(json);
    } catch (error) {
      if (error.name !== 'AbortError') {
        console.error('Fetch error:', error);
      }
    }
  };

  fetchData();

  return () => {
    controller.abort(); // 取消请求
  };
}, []);
  1. 自定义 Hook 封装逻辑(推荐复用): 可创建 useIsMounted 或 useAsync 等 Hook 统一处理该问题。

注意:React 18 严格模式下会故意重复 mount/unmount 组件以暴露此类问题,因此正确处理尤为重要。

发表评论 (审核通过后显示评论):

昵称:
邮箱:
内容: