必须清理副作用,包括定时器、事件监听、未完成请求和外部订阅;清理函数需一一对应启动逻辑,依赖数组须完整准确,卸载后更新状态需用标记防止报错。

在 useEffect 中正确清理副作用,核心是每次启动副作用时,都必须配一个对应的清理函数,且这个函数要在组件卸载前、或下一次副作用执行前被调用。不清理的定时器、未取消的请求、未解绑的事件监听,都会导致内存泄漏或报错(比如“Can’t perform a React state update on an unmounted component”)。
哪些副作用必须清理?
以下操作一旦启动,就需显式清理:
- 使用
setInterval或setTimeout—— 清理时调用clearInterval/clearTimeout - 添加 DOM 事件监听器(如
addEventListener)—— 清理时调用removeEventListener - 发起未完成的异步请求(尤其是用
fetch或axios)—— 清理时应中止请求(用AbortController) - 订阅外部状态(如 WebSocket、RxJS Subject、EventEmitter)—— 清理时调用
.close()或.unsubscribe()
清理函数怎么写才可靠?
清理函数必须返回一个普通函数,且该函数内部只处理当前副作用创建的资源。关键点:
- 清理逻辑要与启动逻辑一一对应,不能漏掉某一项(比如只清定时器却忘了移除事件监听)
- 避免在清理函数里调用
setState,除非你确认组件还活着(可用useRef标记是否已卸载) - 异步请求推荐用
AbortController,比手动判断更安全:
useEffect(() => {
const controller = new AbortController();
const fetchData = async () => {
try {
const res = await fetch('/api/data', { signal: controller.signal });
const data = await res.json();
setData(data);
} catch (err) {
if (err.name !== 'AbortError') console.error(err);
}
};
fetchData();
return () => controller.abort(); // 统一中断所有相关请求
}, []);
依赖数组怎么设才不踩坑?
依赖项不是“可选配置”,而是 React 判断“要不要先清理、再重跑”的依据。常见错误:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
- 漏写依赖:effect 内用了
count却没写进依赖数组 → 拿到的是旧值,还可能跳过清理 - 误写对象/数组:写了
[user],但user是新对象引用 → 每次都触发清理+重跑 - 过度依赖:把整个函数放进去,而函数又依赖其他状态 → 触发无限循环
建议:用 ESLint 插件 eslint-plugin-react-hooks 自动检查;对函数依赖,优先用 useCallback 包裹并稳定其依赖。
组件卸载后还能更新状态?加个保险
即使写了清理函数,异步回调仍可能在卸载后执行。稳妥做法是加一个卸载标记:
useEffect(() => {
let isMounted = true;
const timer = setTimeout(() => {
if (isMounted) setData('done');
}, 1000);
return () => {
isMounted = false;
clearTimeout(timer);
};
}, []);
或者用 useRef 存储该标记,更符合 React 的惯用模式。

















