理解 React 中的 useRef(不再困惑)
Source: Dev.to

如果你一直在学习 React,可能已经熟练掌握了 useState 和 useEffect。但随后你会遇到 useRef,这时感觉…有点怪异。
它不会更新屏幕,也不会触发重新渲染。它好像违背了“React 规则”。其实秘密在于,useRef 是你组件的私有、持久化存储。
下面我们来拆解它到底做了什么、为什么有用,以及如何在不头疼的情况下使用它。
useRef 实际是什么
从本质上讲,useRef 为你提供了一个存放值的地方,该值:
- 在渲染之间保持不变
- 可以随时更改
- 更改时不会导致重新渲染
const myRef = useRef(initialValue);
React 会给你一个看起来像这样的对象:
{ current: initialValue }
关键在于 .current。值就存放在那里。React 只会创建一次这个对象,并在每次渲染时返回同一个对象。
为什么 useRef 会存在
你可能会想:“为什么不直接用 useState 来处理所有事情?” 区别在于可见性。
- State 用于你希望用户看到的东西(UI)。当状态改变时,React 会重新绘制屏幕。
- Refs 用于你想在幕后记住的东西。当 ref 改变时,React 保持沉默,不会对 UI 做任何操作。
典型的 ref 使用场景:
- 一个 DOM 元素
- 一个定时器 ID
- 一个 WebSocket 连接
- 一个先前的值
- 一个标记或计数器
把这些放进 state 会导致不必要的重新渲染。使用普通变量又会在每次渲染时被重置。useRef 正是为了解决这个问题,它提供了记忆功能而不会触发渲染。
最常见的用法:访问 DOM
这是大多数人第一次接触 useRef 的地方。
import { useRef } from "react";
function MyComponent() {
const inputRef = useRef(null);
return (
<>
inputRef.current.focus()}>
Focus
);
}
React 会把真实的 DOM 输入元素放入 inputRef.current,从而让你可以直接调用诸如 focus() 之类的方法。这是正确的做法,用于:
- 聚焦输入框
- 将元素滚动到可视区域
- 测量宽度或高度
- 控制视频或音频元素
DOM 节点不应该放在 state 中;ref 才是合适的工具。
在渲染之间保持值(不触发重新渲染)
有时你需要记住某些东西,但不想让 React 介入。例如:统计组件渲染了多少次。
const renderCount = useRef(0);
renderCount.current += 1;
这个值:
- 在每次渲染时增加
- 永不导致重新渲染
- 永不重置
可用于计数器、标记、缓存数据或内部账务处理。
跟踪先前的值
React 默认不会提供“先前的状态”。useRef 填补了这个空白。
function MyComponent({ value }) {
const prevValue = useRef(value);
useEffect(() => {
prevValue.current = value;
}, [value]);
// now prevValue.current holds the previous `value`
}
value 是当前值,而 prevValue.current 是先前的值。这个模式简洁、可预测且被广泛使用。
存储外部对象
有些东西根本不属于 React:
- WebSocket 连接
AbortController实例- 观察者
- 第三方库对象
function useWebSocket(url) {
const socketRef = useRef(null);
useEffect(() => {
socketRef.current = new WebSocket(url);
return () => socketRef.current.close();
}, [url]);
return socketRef.current;
}
这些对象需要一个稳定的存放位置;useRef 提供了它。把它们放入 state 是设计错误。
useRef 不是
- 它 不是
useState的替代品。 - 它 不是 响应式的,不应用于更新 UI。
如果 UI 依赖于某个值,请使用 state。如果 React 不需要了解它,请使用 ref。遵循此规则可以避免大多数错误。
简单的思维模型
把 useRef 想象成附着在组件上的一个小盒子:
- React 渲染组件。
- 盒子在多次渲染之间保持不变。
- 你可以把任何东西放进盒子里(
.current)。 - React 完全忽略这个盒子。
就是这么回事。
最后思考
useRef 并不复杂——它很直接。它为你提供了一个存放 React 不该管理的东西的地方。只要你明白了这一点,它就不再显得“高级”,而是显而易见。当你真正领悟到它的用法时,你的组件将会:
- 更简洁
- 更可预测
- 更易于理解
这不是魔法——这是为合适的工作使用合适的工具。