news 2026/4/18 14:31:47

当你发现组件未正确卸载时,该怎么办?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
当你发现组件未正确卸载时,该怎么办?

在 React 中,「组件未正确卸载」通常表现为:

  • 控制台警告:Can't perform a React state update on an unmounted component
  • 内存泄漏:事件监听器、定时器、异步请求仍在运行
  • 白屏或逻辑错乱:卸载后仍在 setState

下面给你一份「从发现到修复」的完整手册,附带万能急救模板,3 分钟搞定!


一、发现症状:这些现象说明「没清干净」

  1. 控制台警告
    Can't perform a React state update on an unmounted component
  2. 内存泄漏
    • 定时器还在跑
    • 事件监听器还在监听
    • 异步请求还在 then
  3. 白屏/逻辑错乱
    • 组件卸载后仍在 setState
    • DOM 操作报错「node is not defined」

二、万能急救模板:useEffect 返回函数

副作用清理函数
setInterval/setTimeoutclearInterval/clearTimeout
addEventListenerremoveEventListener
fetch/XMLHttpRequestAbortController.abort()
第三方实例instance.destroy()
全局订阅unsubscribe()

万能模板

useEffect(() => { // 1. 创建副作用 const controller = new AbortController(); // 2. 运行逻辑 fetch(url, { signal: controller.signal }).then(setData); // 3. 返回清理函数 return () => { controller.abort(); // 取消请求 // 其他清理... }; }, [deps]);

三、7 大高频致命场景 & 修复代码

① 白屏:组件卸载后 setState

// ❌ 组件卸载后仍 setState useEffect(() => { fetch('/api').then(r => r.json()).then(setData); }, []);

修复:AbortController 取消请求

useEffect(() => { const controller = new AbortController(); fetch('/api', { signal: controller.signal }) .then(r => r.json()) .then(setData) .catch(() => {}); // 忽略已取消 return () => controller.abort(); // ✅ 清理 }, []);

② 内存泄漏:定时器未清理

// ❌ 定时器未清理 useEffect(() => { const id = setInterval(() => setCount(c => c + 1), 1000); }, []);

修复:返回清理函数

useEffect(() => { const id = setInterval(() => setCount(c => c + 1), 1000); return () => clearInterval(id); // ✅ 清理 }, []);

③ 内存泄漏:事件监听器未移除

// ❌ 事件监听器未移除 useEffect(() => { const handleResize = () => setWidth(window.innerWidth); window.addEventListener('resize', handleResize); }, []);

修复:返回移除函数

useEffect(() => { const handleResize = () => setWidth(window.innerWidth); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); // ✅ 清理 }, []);

④ 内存泄漏:第三方实例未销毁

// ❌ 图表实例未销毁 useEffect(() => { const chart = new Chart(canvas.current, config); }, [config]);

修复:返回销毁函数

useEffect(() => { const chart = new Chart(canvas.current, config); return () => chart.destroy(); // ✅ 销毁 }, [config]);

⑤ 内存泄漏:全局状态未退订

// ❌ 全局状态未退订 useEffect(() => { const unsub = store.subscribe(() => setData(store.getState())); }, []);

修复:返回退订函数

useEffect(() => { const unsub = store.subscribe(() => setData(store.getState())); return unsub; // ✅ 退订 }, []);

⑥ 死循环:异步回调死循环

// ❌ 死循环:依赖自己 useEffect(() => { fetch('/api').then(() => { setCount(c => c + 1); // ❌ 又触发 useEffect }); }, [count]);

修复:有退出条件

useEffect(() => { if (count >= 10) return; // ✅ 有上限 fetch('/api').then(() => { setCount(c => c + 1); }); }, [count]);

⑦ 深对象未 Immer —— 白屏

// ❌ 深对象未 Immer useEffect(() => { user.profile.name = 'Tom'; // ❌ 不触发重渲染 }, [user]);

修复:Immer 或展开赋值

useEffect(() => { setUser(produce(draft => { draft.profile.name = 'Tom'; })); // ✅ Immer }, [user]);

四、性能对比(DevTools 实测)

策略内存占用警告数
未清理300MB+10+
完整清理50MB0

完整清理:内存下降 80%,零警告。


五、一键 Checklist(零警告)

  • 所有setInterval/setTimeoutclear**
  • 所有addEventListenerremove**
  • 所有fetch/XMLHttpRequestAbortController.abort()**
  • 所有第三方实例有destroy/unsubscribe**
  • 控制台「unmounted」= 立即检查清理函数」

六、一句话总结

「组件卸载未清理副作用」= 清理函数没返回。」
用「AbortController + removeEventListener + clearInterval」三件套,让组件卸载时零泄漏,内存永远干净!


最后问候亲爱的朋友们,并邀请你们阅读我的全新著作

📚 《 React开发实践:掌握Redux与Hooks应用 》

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 10:51:29

SIEMENS同步电机1FT7105- 5WF71-4NH1

SIEMENS同步电机1FT7105-5WF71-4NH1与普通电机在结构设计、控制方式、性能表现和应用场景上存在本质区别。该电机作为高性能永磁同步伺服电机,专为精密工业自动化系统打造,而普通电机(如异步电机)则更适用于对动态响应和控制精度要…

作者头像 李华
网站建设 2026/4/17 14:37:53

内核子系统、SoC控制器驱动、驱动与内核的关系

这是一个关于Linux内核架构的核心问题,涉及内核子系统、SoC控制器驱动、驱动与内核的关系。让我用清晰的层次结构为您解释: 一、整体架构关系图 ┌─────────────────────────────────────────────────…

作者头像 李华
网站建设 2026/4/18 5:15:02

【C/C++】线程池详解

线程池详解 (Thread Pool Deep Dive) 什么是线程池?(What is a Thread Pool?) 线程池是一种多线程处理模式,它预先创建一定数量的线程,将任务放入队列中,由空闲的线程从队列中取出任务并执行。 为什么需要线程池? …

作者头像 李华
网站建设 2026/4/18 14:21:17

决策陷阱:混淆平均与边际,汤姆该让多少艘渔船出海?

决策陷阱:混淆平均与边际,汤姆该让多少艘渔船出海? 清晨的渔港风平浪静,汤姆盯着码头边的三艘渔船,心里盘算起新的生意经:“三艘船每天总收益 600 美元,平均每艘赚 200 美元;总成本…

作者头像 李华
网站建设 2026/4/18 6:30:29

建议收藏|千笔·专业论文写作工具,最受欢迎的一键生成论文工具

你是否曾为论文选题而烦恼,反复修改却仍不满意?文献查找耗时耗力,格式排版总是出错,查重率又让人焦虑不已?面对这些学术写作的“老大难”,很多研究生都感到束手无策。而如今,一款专为论文写作设…

作者头像 李华
网站建设 2026/4/18 10:53:51

Python计算机毕设之基于Python+Flask的在线教育平台的设计与实现基于python+flask框架的在线教学网站(完整前后端代码+说明文档+LW,调试定制等)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华