前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库React useEffect cleanup 执行顺序
ReReact副作用与同步

React Effect 挂载、依赖更新和卸载时怎样建立与清理?

一轮同步由建立和清理配对;依赖变化时先结束旧一轮,再用新输入开始下一轮。

前端进阶之旅 · 一题精讲更新于 2026.09.06
React#副作用与同步#Effect#异步编程
先看核心答案
理解线索

从一轮资源持有过程理解顺序

  1. 建立 A依据文档 A 的参数创建订阅,保存本轮句柄
  2. 切换到 B先释放 A 的句柄,再建立 B 的订阅
  3. 结束展示释放最后一轮仍持有的订阅

这里讨论单个 Effect 的资源配对,不把跨组件的执行顺序当成协调业务的接口。

核心回答

先记住这个答案

组件首次提交后,Effect 建立同步。如果后续提交中依赖发生变化,React 先运行上一轮返回的 cleanup,它捕获的是上一轮的输入与资源,然后运行新一轮 setup。卸载时也需要清理仍然存在的同步。普通依赖未变的更新不会仅因组件重新渲染就重建该 Effect。理解时最好把它看成多个独立的同步周期,而不是把清理简单等同于组件卸载。

  • cleanup 不只在卸载时运行,也在重同步前运行
  • 清理应释放本轮建立的资源,而不是猜测当前资源
  • 开发 Strict Mode 会检查建立与清理是否能正确配对

切换文档时,清理函数为什么仍然知道旧 ID

Effect 内创建的 cleanup 是那一轮执行时形成的闭包,它可以直接引用同轮的连接或取消函数。文档 ID 从 A 变成 B 后,清理 A 的逻辑无需从最新 props 再判断要关闭哪个连接;它已经持有本轮创建的资源。随后新的 setup 才根据 B 建立下一轮。

如果把连接放进共享可变变量,又在清理时通过这个变量读取,后续代码可能已经把它改成另一个资源,导致关闭错误连接。通常更清楚的写法是在 Effect 内创建局部句柄,并让返回的函数关闭同一个句柄。确需跨流程共享时,也必须说明所有权与替换规则。

依赖未变与重新挂载是两种不同情况

组件因无关状态更新而重新渲染,如果这一 Effect 的依赖仍相同,就无需因为这次普通更新重新建立同步。可是一旦组件被移除再重新挂载,新的实例就需要新的同步周期,即使业务参数与之前相同。key 改变造成的身份变化也可能引起这种重建。

开发 Strict Mode 的额外建立、清理检查用于暴露不对称资源管理。例如建立了两个监听,却只移除一个,重复挂载后便可能出现同一事件处理两次。用 ref 标记跳过第二次建立只是在遮住检查信号,并没有证明真实路由切换时资源会正确释放。

异步工作还需要处理清理后的迟到结果

cleanup 可以取消订阅、清理定时器,或向可取消请求发送中止信号,但不代表所有外部工作都已停止。服务端可能已经完成操作,某些 API 也不支持取消。因此接收异步结果时还需要判断它是否属于当前有效的一轮,避免旧文档结果写入新文档界面。

也不要把 Effect 函数直接声明成 async 后返回 Promise,把它误当作清理函数。可以在 Effect 内启动异步函数,同时同步返回清理逻辑;对取消、错误与当前轮次分别处理。验证时应故意让旧请求比新请求晚返回,检查页面是否仍显示当前对象的数据。

回答前,多想一步

容易答错的地方

cleanup 执行说明组件一定卸载了
依赖变化引起重同步前也会清理旧一轮,开发检查也可能触发清理。日志应带上具体资源和参数,才能区分是在切换同步对象还是结束整个组件生命周期。
清理时把所有共享资源都关闭最保险
共享资源可能仍被其它组件使用。清理应释放本轮拥有的句柄或订阅份额,连接池与全局服务则按其协议管理,不能由一个消费者卸载就无条件销毁所有者的资源。
试着用自己的话回答

面试官还会怎么问?

cleanup 可以读取最新 state 吗?

它直接闭包捕获的是对应渲染与建立周期的值。确实需要读取额外最新信息时,要明确使用何种机制以及为什么需要,但释放本轮资源通常应依据本轮句柄。

空依赖的 Effect 还需要 cleanup 吗?

需要与否由它是否建立了应释放的资源决定。空数组不会让事件监听或定时器自动消失,组件卸载时仍需正确结束这些外部关系。

多个 Effect 能靠执行顺序传递中间结果吗?

不宜把隐式顺序当成业务协议。存在同一资源生命周期依赖的工作可放在清楚的同一过程里;独立过程则各自声明输入,避免通过可变共享变量耦合。

从一道题,走向一组知识

把知识连起来

副作用与同步

React Effect 忘记清理订阅,路由切换后会出现什么问题?

进一步看遗漏订阅清理如何造成路由返回后的重复处理。

副作用与同步

useEffect 不写依赖、空数组和具体依赖有什么区别?

重新同步是否发生,还取决于实际依赖项的比较结果。

参考资料

  • Lifecycle of Reactive Effects
  • React useEffect

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 切换文档时,清理函数为什么仍然知道旧 ID
  3. 依赖未变与重新挂载是两种不同情况
  4. 异步工作还需要处理清理后的迟到结果
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑