前端进阶之旅前端进阶之旅
  • 基础篇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 ref current 渲染读写纯度惰性初始化
ReReactHooks 与复用

React 为什么不应在渲染时读写 ref.current,惰性初始化为什么是例外?

ref 是可变容器,渲染却需要可以安全重复计算;两者交叉使用时必须明确数据何时会变化。

前端进阶之旅 · 一题精讲更新于 2026.09.06
React#Hooks 与复用#缓存#容器化
先看核心答案读代码示例
理解线索

按阶段划分容器操作

  1. 初始化为尚未建立的内部容器设置确定初值
  2. 事件操作在用户动作里读取和修改命令式数据
  3. 渲染展示使用 state 提供的可追踪值生成界面

示例初始化的是普通 Map,没有网络、计时器或订阅;需要释放的外部资源不能照搬到渲染阶段。

核心回答

先记住这个答案

通常不应在渲染中读取或修改 ref.current 来决定可见输出,因为 ref 变化不触发 React 更新,且渲染可能被重复、暂停或放弃。渲染中写入可变容器会让一次未提交的尝试留下外部可见变化,读取则可能使相同 props 与 state 得到不同输出。官方允许一类可预测的初始化:只在尚未初始化时写入确定的初始对象,后续渲染不重新改变它。这个例外不允许在渲染中任意同步最新 props、递增计数或创建外部订阅。

  • 界面需要的数据使用 props 和 state
  • 一般命令式 ref 读写放在事件或合适生命周期
  • 初始化例外要求可预测且没有外部副作用

为什么 render 中递增 ref 不是可靠渲染计数

如果每次函数执行都修改 ref.current,再把它显示到页面,开发检查或未提交的渲染也会改变这个计数。数值描述的既不是稳定业务状态,也不一定是用户真正看到的提交次数,后续行为会依赖框架调用函数的过程。

下面只在空容器时完成可预测初始化,后续计数变化发生在点击事件里,并通过 state 更新可见文字。Map 用于保存命令式数据,state 承担渲染输入,避免期待 current 变化自动通知页面。

初始化内部容器,事件中修改并更新展示状态TSX
import { useRef, useState } from 'react';

export default function CachedClicks() {
  const cache = useRef<Map<string, number> | null>(null);
  const [visibleCount, setVisibleCount] = useState(0);
  if (cache.current === null) cache.current = new Map();
  function remember() {
    const store = cache.current!;
    const next = (store.get('clicks') ?? 0) + 1;
    store.set('clicks', next);
    setVisibleCount(next);
  }
  return <button onClick={remember}>已记录:{visibleCount}</button>;
}

连续点击得到一、二,普通重渲染不会重新创建正在使用的 Map。示例用 state 展示计数;对于只需要一个计数的实际业务,直接使用 state 会更简单,这里用两者对照职责。

可预测初始化不等于构造函数可以做任何事

new Map 只是创建当前组件内部的普通数据容器,没有对外订阅或请求。若所谓初始化函数会打开连接、登记全局监听或修改共享对象,即使写在 current 为 null 的判断里,也可能被重复渲染或丢弃的实例影响。

直接写 useRef(createExpensiveObject()) 时,参数表达式仍会在函数每次执行时求值,虽然 React 只使用初次 ref 值。需要避免重复构造且初始化本身纯粹时,才考虑空值守卫;不要把它当成通用资源生命周期替代品。

最新值同步与可见状态应采用不同设计

每次 render 都把 props 写进 ref,以供外部回调读取,可能把尚未提交的值暴露给当前生效的外部过程。应先明确回调需要的是已提交值还是某次事件输入,再选择 Effect、Effect Event 或其他符合所用版本约束的方案。

如果 current 中的数据本身决定页面显示,把它藏在 ref 会让 React 无法知道何时重新计算。外部 store 应提供订阅与快照接口;组件内部显示数据通常进入 state。不要依靠其他偶然更新顺便把可变引用中的新内容刷新出来。

回答前,多想一步

容易答错的地方

useRef 不触发渲染,所以 render 里修改它一定安全
不触发更新只是容器特性,并不提供纯度保证;写入仍可能影响未来读取和其他回调,使未提交渲染留下可观察变化。
只要加一个 null 判断就能在 render 创建连接
null 守卫不能把外部副作用变成纯初始化。连接、观察器和订阅仍需要明确的建立与清理阶段,不能依赖只运行一次的假设。
试着用自己的话回答

面试官还会怎么问?

为什么示例中读取 cache.current 的空值检查允许?

它属于可预测初始化的窄例外,只在尚未建立内部容器时给出确定初值,之后不随每次渲染改写。不能把这个例外扩展成任意读取可变业务状态。

读取 ref 的事件处理器能看到后续写入吗?

读取时会访问同一个可变容器的当前内容,这与闭包捕获某次 state 值不同。但它仍不会自动驱动界面,展示变化需要明确的状态更新。

怎样保存需要在页面显示的前一个值?

应在状态转换中明确保存 previous 与 current,或在合适上层维护历史。不要为了省一个状态字段,在 render 中偷读随后又会被 Effect 修改的 ref。

从一道题,走向一组知识

把知识连起来

Hooks 与复用

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

区分可变容器与驱动界面更新的状态

Hooks 与复用

React 用 ref 保存前一个值时,“上一次渲染”和“上一次提交”有什么区别?

需要历史值时明确记录的阶段与用途

参考资料

  • React:useRef caveats and initialization
  • React:Components and Hooks must be pure

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

本题目录
  1. 先记住这个答案
  2. 为什么 render 中递增 ref 不是可靠渲染计数
  3. 可预测初始化不等于构造函数可以做任何事
  4. 最新值同步与可见状态应采用不同设计
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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