useref原理
useRef 的原理是:React 在内部通过 Hook 链表机制,在每次组件渲染时都返回同一个 ref 对象({ current }),这个对象的值可以随意修改且不会触发重新渲染,常用于保存 DOM 引用或跨渲染周期的变量。
✅ 一、useRef 是什么?
是一个React Hook,返回一个具有 .current
属性的可变对象,用于存储可变值或 DOM 引用
const ref = useRef(initialValue);
返回一个 可变对象,其结构为:
{ current: initialValue }
你可以通过
ref.current
读取或修改它的值修改
.current
不会触发组件重新渲染在组件的多次渲染之间,ref 对象本身保持不变(引用不变)
常用于:
获取 DOM 元素引用
保存可变值(如定时器 ID、上一次的值)而不引起渲染
✅ 二、useRef 的核心原理
🎯 核心思想一句话:
useRef 是 React 提供的一个 Hook,它在每次组件渲染时,都返回同一个 ref 对象(引用不变),这个对象的 .current 属性是可变的,但修改它不会触发重新渲染。
1️⃣ useRef 返回的是一个稳定的对象引用(每次渲染返回同一个对象)
React 在内部实现 useRef
时,会:
在组件首次渲染时,创建一个普通的 JavaScript 对象:
{ current: initialValue }
将该对象保存在 React 内部的 Hook 链表中(与当前组件关联)
在后续每次重新渲染时,React 都会返回同一个 ref 对象(即内存地址不变)
✅ 这就是为什么你可以在多次渲染中拿到同一个
ref
对象,且可以放心地用它存一些“长期不变”的数据或 DOM 引用。
为什么返回稳定对象引用?
React 内部在 Hook 链表中保存了该 ref 对象,每次渲染都返回同一个引用,不会新建
2️⃣ 修改 .current
不会触发重新渲染
React 的渲染机制是:
当调用 状态设置函数(如 setState、useReducer、useContext 变化) 时,React 知道状态发生了变化,就会安排一次 重新渲染
但 useRef 的 .current 是一个普通属性,React 不会监听它的变化,也不会因为它的改变而触发重新渲染
✅ 所以,无论你如何修改
ref.current
,React 都不会重新执行组件函数,页面也不会刷新。
3️⃣ useRef 的实现依赖 React 内部的 Hook 机制
React 的每一个 Hook(如 useState、useEffect、useRef) 都是按照 调用顺序 保存在 当前组件 Fiber 节点上的一个单向链表(Hook链表) 中的。
当组件重新渲染时,React 会:
按照 Hook 的调用顺序,从链表中依次取出上一次渲染时保存的 Hook 状态
对于
useRef
,React 会返回上一次创建的那个{ current: ... }
对象,而不是新建一个
✅ 这就是为什么
useRef
在多次渲染中始终返回同一个对象 —— 因为 React 内部复用了那个 Hook 对象。
✅ 三、useRef 的简单实现原理(伪代码 / 概念模型)
虽然我们无法看到 React 源码的细节,但可以用伪代码来模拟它的行为:
// 假设这是 React 内部管理 Hook 的方式(简化模型)let hookChain = []; // Hook 链表,按调用顺序保存每次渲染的 Hook 状态function useRef(initialValue) {// 获取当前 Hook 的索引(React 内部通过调用顺序管理)const hookIndex = getCurrentHookIndex(); // 如果是第一次渲染,创建一个新的 ref 对象if (!hookChain[hookIndex]) {hookChain[hookIndex] = { current: initialValue }; // 创建 ref 对象}// 每次渲染都返回同一个 ref 对象return hookChain[hookIndex];
}
✅ 每次调用
useRef()
,React 都会从内部保存的 Hook 数据中,返回你上一次用过的那个 ref 对象,而不是新建一个。
所以无论你调用多少次 useRef()
,只要没有卸载组件,你拿到的都是 同一个对象引用。
✅ 四、useRef 与 useState 的对比(原理层面)
特性 | useRef | useState |
---|---|---|
返回值 | 一个对象: | 一个数组: |
修改方式 | 直接修改 | 调用 |
是否会触发重新渲染? | ❌ 不会 | ✅ 会 |
React 是否追踪其变化? | ❌ 不追踪,不关心 | ✅ 追踪,一旦变化就触发更新 |
底层保存方式 | 保存为普通的 Hook 对象,每次返回同一个引用 | 保存状态值,变化时触发调度与渲染 |
用途 | 保存 DOM 引用、可变值(不触发渲染) | 保存 UI 相关状态(触发渲染) |
✅ 五、深入一点:React Fiber 与 Hook 链表(高级)
在 React 内部的实现中:
每个 函数组件 对应一个 Fiber 节点
每个 Hook(如 useRef、useState) 被调用时,会按照顺序保存到该 Fiber 节点上的一个 Hook 链表(链表结构) 中
每个 Hook 在链表中是一个对象,保存了该 Hook 的状态与逻辑
当组件重新渲染时,React 会 按顺序遍历这个 Hook 链表,恢复上一次渲染的 Hook 状态
✅ 所以
useRef
能够返回同一个对象,是因为 React 内部在 Fiber 上 持久化了该 Hook 的引用,而不是每次渲染都新建。
✅ 六、总结:useRef 的原理和creactref区别
问题 | 原理说明 |
---|---|
useRef 是什么? | React Hook,返回一个具有 |
为什么每次渲染返回同一个 ref 对象? | React 内部在 Hook 链表中保存了该 ref 对象,每次渲染都返回同一个引用,不会新建 |
修改 | ❌ 不会,React 不监听 .current 的变化,它只是一个普通属性 |
useRef 的底层依赖是什么? | React 的 Fiber 架构 + Hook 链表机制,保证每次渲染能正确恢复上一次的 Hook 状态 |
useRef 和 useState 有什么本质区别? | useState 的状态变化会触发重新渲染,而 useRef 的 .current 变化不会 |