前端进阶之旅前端进阶之旅
  • 基础篇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 对象函数依赖反复执行
ReReact副作用与同步

useEffect 依赖对象或函数时,为什么可能每次渲染都重跑?

组件函数每次执行时创建的对象与函数通常有新引用,Effect 会把这样的依赖视为发生变化。

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

先找出配置真正由什么决定

  1. 业务输入文档 ID、服务地址等决定外部连接目标
  2. 包装构造对象和辅助函数可以在需要它们的同步内部创建
  3. 共享引用多个调用方确需复用时再考虑缓存与完整依赖

移动代码是为了让依赖对应真正的数据关系,不是把仍会变化的输入伪装成静态值。

核心回答

先记住这个答案

Effect 逐项用 Object.is 比较依赖。组件体内创建的配置对象或函数,即使内容看起来相同,也通常不是上次的引用,因此可能使 Effect 在每次提交后重新同步。先看这个对象或函数能否移到 Effect 内部,再依赖实际使用的原始输入;真正静态的内容也可以移到组件外。确实需要在多个地方共享引用时再评估 useMemo 或 useCallback,不能为减少重跑而遗漏真实依赖。

  • 字段相同不等于对象或函数引用相同
  • 优先调整构造位置,减少无意义的依赖身份
  • 缓存依赖仍需完整,缓存不是资源生命周期保证

一次输入框更新为何让外部连接重建

假设页面同时有搜索输入和文档预览连接,组件体每次构造包含 documentId 的 options 对象,Effect 依赖 options。用户只是输入搜索词,documentId 没变,但 options 已经是新对象,因此清理与重连仍可能发生。把问题归咎于 React 无故重跑,会漏掉这个明确的引用变化。

若 options 只被该 Effect 使用,可以在 Effect 内根据 documentId 创建它,让依赖直接表达 documentId。这样每一轮同步仍有独立配置对象,但构造发生在真正需要建立同步的时候,无关输入不再仅因包装引用不同就要求重连。

辅助函数也要区分定义位置与实际输入

组件中定义 buildOptions 函数,再让 Effect 依赖它,也可能出现同样问题。函数每次重新创建,即使函数体文本完全一样,引用仍不同。如果这个函数只为该 Effect 服务,可以将它放入 Effect;若只是纯粹根据参数生成配置,也可在组件外定义并显式传入必要参数。

不能把读取当前 props 的函数直接搬到模块顶层,然后依赖一个共享可变变量提供最新值,这会混淆组件实例与请求范围。移出组件的代码应真正独立于该实例的渲染输入,或把需要的值作为参数传递,而不是用全局变量绕过依赖工具。

什么时候缓存比移动构造更合适

一个配置对象同时传给第三方适配层和多个子组件,确实需要稳定共享身份时,可以评估 useMemo;回调被多处使用时也可评估 useCallback。缓存依赖必须涵盖读取的响应式输入,参数变化时产生新引用通常正是正确行为,不能一律视为性能问题。

把整个对象 JSON.stringify 后作为依赖看似比较内容,却引入序列化成本与语义限制,例如函数、循环引用和字段表示规则。深比较同样需要清楚的对象类型与成本边界。更可维护的起点通常是缩小 Effect 的职责,让它只依赖自己实际需要且容易比较的输入。

回答前,多想一步

容易答错的地方

为了不重连,删掉对象依赖就行
Effect 若仍读取这个对象,后续字段变化就可能无法触发必要同步。应该修改代码结构并保持依赖诚实,而不是只删除工具提示的条目。
使用 useMemo 后资源一定不会再重建
缓存用于减少重复计算,不能承担永久资源身份的正确性保证。Effect 的清理与建立仍应可安全重复,真正资源所有权要由明确的生命周期或稳定持有机制管理。
试着用自己的话回答

面试官还会怎么问?

对象 prop 是父组件传来的,该在哪一层修?

先确认子组件需要整个对象还是少数字段。可以让子组件同步直接依赖必要字段,也可以在父层避免无意义的新对象;不要要求所有父组件缓存来掩盖子组件过宽的依赖。

函数体没有读 props,为什么函数依赖还是变?

引用是否相同由函数对象的创建决定,不由函数体是否读取 props 决定。纯工具函数可以在组件外定义,或移到唯一使用它的 Effect 内,减少无关身份变化。

对象内部原地修改,依赖会检测到吗?

如果仍是同一引用,Object.is 不会发现内部字段变化。需要不可变更新或明确的外部订阅机制,不能同时期待引用稳定和任意内部修改都被自动感知。

从一道题,走向一组知识

把知识连起来

副作用与同步

useEffect 为什么必须声明读到的响应式依赖,漏写会怎样?

理解哪些输入必须成为依赖,再决定如何整理对象与函数。

状态共享

Context Provider 的 value 为什么常用 useMemo?

Provider value 也采用 Object.is,引用身份问题在 Context 中同样常见。

参考资料

  • Removing Effect Dependencies
  • React useEffect:对象与函数依赖

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

本题目录
  1. 先记住这个答案
  2. 一次输入框更新为何让外部连接重建
  3. 辅助函数也要区分定义位置与实际输入
  4. 什么时候缓存比移动构造更合适
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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