如何在 React 中正确地使用 useEffect 处理异步请求并避免内存泄漏?
如何在 React 中正确地使用 useEffect 处理异步请求并避免内存泄漏?
在 React 中,使用 useEffect 执行异步操作(如数据请求)时,若组件在请求完成前被卸载,直接更新状态会导致“Can't perform a React state update on an unmounted component”警告,这不仅是一种内存泄漏隐患,也可能引发 bug。
正确做法是使用“取消标记”(cleanup function + abort controller 或布尔标记)来确保只在组件挂载时才更新状态。
示例(使用布尔标记):
import { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
let isMounted = true; // 标记组件是否仍挂载
const fetchData = async () => {
const response = await fetch('/api/data');
const result = await response.json();
if (isMounted) {
setData(result); // 仅在组件未卸载时更新状态
}
};
fetchData();
return () => {
isMounted = false; // 组件卸载时清理
};
}, []);
return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
}
更现代的做法是使用 AbortController(适用于 fetch):
useEffect(() => {
const controller = new AbortController();
const fetchData = async () => {
try {
const response = await fetch('/api/data', {
signal: controller.signal
});
const result = await response.json();
setData(result);
} catch (error) {
if (error.name !== 'AbortError') {
console.error('Fetch error:', error);
}
}
};
fetchData();
return () => {
controller.abort(); // 取消正在进行的请求
};
}, []);
解析:
- useEffect 的返回函数会在组件卸载或依赖变化前执行,是清理副作用的理想位置。
- 使用 isMounted 或 AbortController 可有效防止对已卸载组件的状态更新。
- AbortController 是浏览器原生支持的取消机制,更符合标准,推荐用于 fetch 请求;而 isMounted 更通用,适用于非 fetch 的异步操作(如 setTimeout、第三方库等)。
注意:React 18 严格模式下,useEffect 可能会运行两次(开发环境),此时 cleanup 也会执行,因此确保 cleanup 逻辑健壮非常重要。

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