前端进阶之旅前端进阶之旅
  • 基础篇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 useRef 和 useState 区别
ReReactHooks 与复用

React 中 useRef 和 useState 都能跨渲染保存数据,二者的本质区别和适用场景是什么?

界面需要随值改变而更新就用 state;只保存不参与渲染的可变句柄,通常可以用 ref。

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

先看数据是否决定界面,再选择存储方式

  1. 界面状态输入文本、选中项、请求状态需要通知渲染
  2. 可变句柄定时器 ID 和 DOM 节点供事件或同步逻辑使用
  3. 渲染快照事件闭包关联创建它的那次渲染中的状态

ref 可以在事件或 Effect 中更新;渲染逻辑应保持可重试和可预测。

核心回答

先记住这个答案

useState 保存参与渲染的数据,setter 会安排更新,每次渲染读取的是对应的状态快照。useRef 返回一个跨后续渲染保持身份的对象,修改 current 不会通知 React 重新渲染,适合 DOM 引用、计时器句柄等非展示数据。两者都不会因为函数再次调用而简单丢失,但 ref 的可变性不能替代响应式更新,也不应该成为在渲染中任意读写共享数据的入口。

  • state 更新参与 React 的渲染调度
  • ref.current 改变不会触发重渲染
  • 需要显示的数据不应仅藏在可变 ref 中

稳定保存并不等于自动通知更新

计时器每秒把 ref.current 加一,并不会因此让屏幕上的数字更新。即使组件偶尔因父层变化重新渲染时读到了新数字,这也只是一次碰巧的读取,不能作为可靠的显示机制。相反,通过 state 更新通知 React,界面才与数据变化建立了明确关系。

另一方面,保存 setTimeout 返回的句柄只是为了取消任务,不需要把这个编号展示给用户。用 state 存句柄会增加没有视觉意义的更新,而 ref 可以让后续点击取消的事件拿到同一个实例中保存的句柄。定时器本身仍需在合适的生命周期中清理。

搜索交互中分别保存文本和请求控制器

假设搜索框需要实时显示输入内容,并允许终止上一个请求。输入值属于 state,因为它决定 input 的 value;AbortController 可以保存在 ref 中,供下一次搜索或清理过程取消请求。两种值虽然都跨渲染存在,但改变它们对界面的意义不同。

如果还要显示“正在搜索”,这个布尔状态应进入 state 或由请求状态层提供,而不是仅判断 ref.current 是否存在。请求失败、取消与完成时都要明确更新展示状态。这样的区分能避免控制器已经被清理、界面却仍然显示旧加载状态的问题。

ref 能读最新值,但不能自动解决所有闭包问题

异步回调可能捕获旧渲染里的 state。如果业务明确要求回调执行时读取某个最近提交的值,可以通过受控的 ref 同步建立这个通道,但必须说明何时写入、何时读取。若需要的是“本次点击发生时的值”,原来的快照反而是正确语义,替换成最新 ref 会改变业务含义。

通常不要在渲染过程中任意读写 current 来驱动结果,否则可能绕过 React 对纯渲染的假设。确定性的懒初始化存在有限例外,但这不意味着可以在 render 中启动计时器或递增全局计数。调试时先检查是否错误地用 ref 存放了本应驱动 UI 的数据,再处理闭包与生命周期。

回答前,多想一步

容易答错的地方

把 ref 理解为性能更好的 state
两者表达的语义不同。用 ref 隐藏应触发更新的数据,可能减少渲染次数,却让界面不再正确;性能优化不能通过切断数据与显示之间的关系来实现。
认为 setter 调用后旧闭包里的变量立即改变
setter 安排的是后续渲染,已经执行中的函数仍然使用该次渲染关联的值。如果下一状态依赖前一状态,可使用函数式更新;是否读取最新值还要根据业务时点选择。
试着用自己的话回答

面试官还会怎么问?

为什么普通局部变量不能替代 useRef?

组件函数重新执行会重新建立局部变量,事件和 Effect 还可能引用不同渲染的闭包。ref 提供与组件实例生命周期关联的稳定对象,让非展示数据有明确的保存位置。

修改 ref 后调用另一个 setState,页面能读到新 ref 吗?

后续渲染可能读到被修改的 current,但这不意味着 ref 已具备订阅能力。依赖另一个无关更新带出 ref 的变化会让数据流难以解释,展示信息应直接建立受支持的状态关系。

组件换 key 后原来的 ref 还在吗?

对应实例重建后,会建立新的 ref 对象并重新初始化。若某个数据需要跨多次组件重建保留,就应把它放到更高层生命周期中,而不是假定 ref 可以超出组件身份永久存在。

从一道题,走向一组知识

把知识连起来

副作用与同步

React 中由 props/state 计算出的值为什么通常不该放进 Effect 再 setState?

参与渲染的数据也未必需要单独 state,可以继续判断它是否只是一个派生值。

Hooks 与复用

为什么 React 的 Hooks 必须在每次渲染中以完全相同的顺序调用,不能放进条件或循环里?

ref 与 state 都依赖组件的 Hook 调用结构,继续理解为什么不能随条件改变调用位置。

状态共享

React 的 createContext 传入的默认值在什么情况下才会被组件实际读到?

状态需要跨组件共享时,继续看 Context 从哪个提供者读取值,以及默认值何时才会生效。

参考资料

  • useRef

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

本题目录
  1. 先记住这个答案
  2. 稳定保存并不等于自动通知更新
  3. 搜索交互中分别保存文本和请求控制器
  4. ref 能读最新值,但不能自动解决所有闭包问题
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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