别再只盯着 useState 了!useRef 才是你组件里最稳的“老黄牛”
在 React 的 Hooks 家族中,useState 是当之无愧的流量明星——谁不为它的响应式能力疯狂打 call?但今天,我们要把聚光灯转向一个低调到几乎被忽略的角色:useRef。
它不像 useState 那样一变就让整个组件重渲染;也不像 useEffect 那样自带“生命周期光环”。它就像公司里那个从不抢功劳、却总在关键时刻修好服务器的运维大哥——默默奉献,稳如泰山。
今天,我们就来揭开 useRef 的神秘面纱,看看这个“可持久化引用对象”到底有多香!
🎭 useRef 是什么?和 useState 有啥区别?
先上结论:
-
相同点:两者都是用来“存东西”的容器。
-
不同点:
useState存的是响应式状态——一改就触发重渲染。useRef存的是可变但非响应式的引用——改了天王老子也不会重渲染组件。
const [count, setCount] = useState(0); // 改了 → 组件重新渲染
const ref = useRef(0); // ref.current = 1; → 组件纹丝不动
你可以把 useRef 想象成一个带锁的小抽屉:你随时能往里塞东西、取东西,但 React 根本不在乎你动没动它——除非你自己主动去用它。
🛠️ 场景一:获取 DOM 元素?交给 useRef!
在 Vue 里,我们用 ref="xxx" + this.$refs.xxx 来操作 DOM。
在 React 函数组件里?useRef 就是你的 $refs!
import { useRef, useEffect } from 'react';
export default function App() {
const inputRef = useRef(null);
useEffect(() => {
// 页面挂载后自动聚焦!
inputRef.current?.focus();
}, []);
return <input ref={inputRef} placeholder="我自动聚焦啦~" />;
}
💡 注意:
ref={inputRef}这个写法是 React 的“魔法语法”——当你把useRef对象赋给元素的ref属性时,React 会自动把 DOM 节点塞进ref.current里!
这比 document.getElementById 高级多了——无需 ID,无需全局污染,精准绑定,安全可靠。
⏱️ 场景二:存储“可变但不想触发重渲染”的值
想象一个经典需求:实现一个可启停的定时器。
如果你用 useState 存 intervalId,每次 setInterval 都要更新状态,结果就是——组件疯狂重渲染,性能直接拉胯。
这时候,useRef 就派上用场了!
import { useState, useRef } from 'react';
export default function TimerDemo() {
const [count, setCount] = useState(0);
const intervalId = useRef(null); // 关键:用 useRef 存 ID!
const start = () => {
if (intervalId.current) return; // 防止重复启动
intervalId.current = setInterval(() => {
console.log('tick~~~~');
}, 1000);
};
const stop = () => {
clearInterval(intervalId.current);
intervalId.current = null;
};
return (
<>
<button onClick={start}>开始</button>
<button onClick={stop}>停止</button>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>+1</button>
</>
);
}
✅ 优势:
intervalId.current可以随意修改,不会触发重渲染。- 组件卸载时,记得在
useEffect里清理定时器(避免内存泄漏)!
🔁 useRef vs useState:什么时候该用谁?
| 场景 | 推荐 Hook | 原因 |
|---|---|---|
| 需要 UI 随数据变化而更新 | useState | 响应式,自动触发重渲染 |
| 需要保存一个值,但不希望引起重渲染 | useRef | 非响应式,性能更优 |
| 需要操作 DOM 元素 | useRef | React 官方推荐方式 |
| 需要跨渲染周期保持某个值(如前一次的 props) | useRef | 可持久化,值不会“丢失” |
🧠 小技巧:
如果你发现某个useState的值只在副作用里用,从来不用于 JSX 渲染,那它很可能应该换成useRef!
🤔 为什么 useRef 不是响应式的?这是缺陷吗?
不是缺陷,是设计哲学!
React 的核心思想是:UI = f(state) 。只有状态变了,UI 才该变。
而 useRef 存的不是“状态”,而是“辅助数据”——比如 DOM 引用、定时器 ID、WebSocket 实例、前一次的计算结果等。这些数据的变化不应该影响 UI 结构。
所以,useRef 的“非响应式”恰恰是它的优点:让你在不打扰 React 渲染机制的前提下,安全地管理副作用相关的可变数据。
🎉 总结:useRef —— React 的“隐形守护者”
- ✅ 获取 DOM 元素?
useRef是官方指定方案。 - ✅ 存储非响应式可变值?
useRef性能更优。 - ✅ 跨渲染周期保持数据?
useRef稳如老狗。 - ❌ 别用它来存需要驱动 UI 更新的数据——那是
useState的地盘!
下次当你写 useEffect 里需要保存某个“中间变量”时,不妨问问自己: “我真的需要让它触发重渲染吗?”
如果答案是否定的——恭喜你,useRef 正在对你微笑 😊
彩蛋:
有人说useRef是 React Hooks 里最被低估的 Hook。
我说:它不是被低估,而是太谦虚——真正的高手,从不喧哗。