🧠 `useRef`:React 中“默默记住状态却不打扰 UI”的利器

在 React 函数组件中,我们常用 useState 来管理会触发页面更新的状态。但有些数据------比如定时器 ID、DOM 节点引用、或上一次的计算结果------我们只想记住它,却不想因为它的变化而重新渲染组件 。这时候,useRef 就派上了大用场。

本文将通过两个典型场景(自动聚焦输入框启动/停止计时器 ),深入讲解 useRef 的核心价值:可变、持久、非响应式


一、useRef 是什么?

useRef 是 React 提供的一个 Hook,用于创建一个可变的、跨渲染周期持久存在的引用对象

ini 复制代码
const ref = useRef(initialValue);
  • 返回值是一个对象:{ current: initialValue }
  • ref.current 可读可写
  • 修改 ref.current 不会触发组件重新渲染
  • 在组件整个生命周期内,ref 对象本身(引用)保持不变

✅ 简单说:useRef 就是一个"私有小盒子",你可以往里存任何东西,React 完全不管,也不会因此重画页面。


二、对比 useState:相同与不同

特性 useState useRef
都能存储数据 ✅ 是 ✅ 是
数据可变 ✅ 通过 setState ✅ 直接赋值 ref.current = ...
是否触发 re-render ✅ 是(响应式) ❌ 否(非响应式)
适合存什么 UI 状态(如表单值、开关) DOM 引用、定时器 ID、上一次的值等

💡 关键区别:

  • useState 是"状态驱动 UI"
  • useRef 是"记住东西,但不影响 UI"

三、场景 1:获取 DOM 元素 ------ 自动聚焦

ini 复制代码
const inputRef = useRef(null);

useEffect(() => {
  inputRef.current.focus(); // 让输入框自动获得焦点
}, []);

return <input ref={inputRef} placeholder="自动聚焦" />;
  • useRef(null) 创建一个初始为空的引用
  • 通过 ref={inputRef},React 将真实的 <input> DOM 元素赋值给 inputRef.current
  • useEffect(相当于 Vue 的 onMounted)中调用 .focus(),实现挂载后自动聚焦
  • 整个过程不涉及状态更新,无额外渲染

✅ 这是 useRef 最经典的用途之一:操作 DOM


四、场景 2:保存定时器 ID ------ 启动与清理

scss 复制代码
const intervalId = useRef(null);

function start() {
  intervalId.current = setInterval(() => {
    console.log('tick~~~~');
  }, 1000);
}

function stop() {
  clearInterval(intervalId.current);
  intervalId.current = null;
}

// 组件卸载时自动清理
useEffect(() => {
  return () => {
    if (intervalId.current) {
      clearInterval(intervalId.current);
    }
  };
}, []);

🔑 为什么必须用 useRef

  • setInterval 返回一个唯一的数字 ID(如 123
  • 我们需要在 stop() 或组件卸载时,用这个 ID 调用 clearInterval 来停止定时器
  • 如果用普通变量(如 let id = null),每次组件 re-render(比如点击 count++)都会重置为 null,导致无法清除定时器 → 内存泄漏!
  • useRefcurrent在多次渲染中保持不变,完美保存了定时器 ID

✅ 核心优势:

  • 记住上一次的状态(定时器 ID)
  • 不因 count 变化而丢失
  • 修改它不会触发不必要的渲染
  • 避免陷入"渲染 → 启动新定时器 → 再渲染"的死循环

五、useRef 的本质:可持久化的引用对象

从你提供的代码可以看出:

scss 复制代码
useEffect(() => {
  console.log(intervalId.current);
}, [count]); // 即使 count 变化,intervalId.current 依然保留之前的值

这证明了:

  • useRef 创建的对象在组件整个生命周期中是同一个引用
  • 它的 .current 属性可以被任意修改
  • React 完全忽略对它的修改,既不响应,也不追踪

🎯 这正是 useRef 的设计哲学: "我只负责记住,不负责通知。"


六、总结:useRef 的三大核心用途

  1. 引用 DOM 元素
    → 如自动聚焦、滚动到指定位置、获取尺寸等
  2. 存储可变但无需触发渲染的数据
    → 如定时器 ID、WebSocket 实例、请求取消 token 等
  3. 保存上一次的 props 或 state(进阶)
    → 虽然你未提及,但原理相同:用 useRef 记住旧值,在 useEffect 中更新

✅ 最后提醒

  • 不要用 useRef 存放 UI 状态 (如表单值),那应该用 useState
  • 不要依赖 useRef 触发更新,它天生是非响应式的
  • 善用 useRef + useEffect 清理函数,避免内存泄漏

💬 正如你的注释所说:
useRef ------ 默默奉献的存储能力
useState ------ 响应式的状态驱动

掌握这两者的分工,你就掌握了 React 函数组件状态管理的精髓!

相关推荐
用户680325754322 小时前
vue 上传文件到 OSS
前端
POLITE32 小时前
Leetcode 3.无重复字符的最长子串 JavaScript (Day 4)
javascript·算法·leetcode
明月_清风2 小时前
GSAP + ScrollTrigger 实现滚动驱动动画详解
前端
代码猎人2 小时前
如何实现一个三角形
前端
kuilaurence2 小时前
Node.js 中使用env文件
javascript
龙国浪子2 小时前
从点到线,从线到画:Canvas 画笔工具的实现艺术
前端·electron
代码猎人2 小时前
什么是margin重叠,如何解决
前端
TeamDev2 小时前
使用 Vue.js 构建 Java 桌面应用
java·前端·vue.js
DongHao2 小时前
跨域问题及解决方案
前端·javascript·面试