React useRef
React 中用于保存不参与渲染的可变值,或引用 DOM 节点的 Hook。
[!info] related notes
- 所属 MOC: React MOC
- 上位主题: react-hooks, react
- 易混淆概念: React 中的 props、state 和 ref, react-use-state
React useRef
useRef 用于保存一个在组件多次渲染之间持续存在、但变化后不会触发重渲染的值。
一句话定义
如果一个值需要记住,但它的变化不应该驱动界面更新,就优先考虑 useRef。
最常见的两个用途
引用 DOM 节点
例如聚焦输入框、读取元素尺寸、配合第三方库初始化。
保存可变值
例如定时器 ID、上一次值、某个实例对象。
为什么它和 state 不一样
state变化会触发重渲染ref.current变化不会触发重渲染
最小例子
const inputRef = useRef<HTMLInputElement | null>(null)
const timerRef = useRef<number | null>(null)
两个典型例子
例子 1:聚焦输入框
function SearchInput() {
const inputRef = useRef<HTMLInputElement | null>(null)
useEffect(() => {
inputRef.current?.focus()
}, [])
return <input ref={inputRef} />
}
例子 2:保存最新值给长期回调读取
function Counter() {
const [count, setCount] = useState(0)
const countRef = useRef(count)
useEffect(() => {
countRef.current = count
}, [count])
useEffect(() => {
const id = window.setInterval(() => {
console.log(countRef.current)
}, 1000)
return () => window.clearInterval(id)
}, [])
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
这里 ref 的价值是:让长期存在的定时器回调可以读到最新值,而不必每次都重建定时器。
它为什么适合解决闭包旧值问题
因为 ref 对象本身在多次渲染间是稳定的,而你可以手动更新 ref.current。
所以:
- 回调可以长期存在
- 读取到的却是最新的
current
什么时候不要用它
- 只要某个值变化后应该更新界面,就不要偷懒用
useRef - 不要把本来应该进入 React 数据流的状态藏进 ref
- 不要把
ref.current的变化误以为能自动触发渲染
一个最实用的判断题
如果这个值变化后,用户应该看到 UI 跟着变化,用 state。
如果这个值只是需要被记住、被读取、被 cleanup,但不需要刷新界面,用 ref。
最短记忆方式
useRef 是“不参与渲染的记忆格”,不是 state 的替代品。
面试要点
来自 react-use-ref-interview-question 的面试视角整理。
一句话回答
useRef 适合保存跨渲染周期持续存在、但变化后不需要触发界面更新的值;而 state 变化后会驱动组件重新渲染。
最稳的回答主线
useRef常用于 DOM 引用、定时器 ID、上一次值- state 适合真正影响 UI 的数据
- 不要把本该进入 React 数据流的状态偷偷塞进 ref
一个更完整的面试表达
可以这样答:
useRef的核心价值是“跨渲染记住一个值,但它变化后不会触发组件重新渲染”。所以它特别适合两类场景:一类是拿 DOM 节点,另一类是保存 timer id、上一次值、第三方实例这种不参与渲染的可变值。
和 state 最大的区别是,state 用来驱动 UI,ref 用来保存不驱动 UI 的记忆。
一个最常见的例子
function SearchInput() {
const inputRef = useRef<HTMLInputElement | null>(null)
useEffect(() => {
inputRef.current?.focus()
}, [])
return <input ref={inputRef} />
}
这里如果不用 ref,就很难拿到真实 DOM。
一个加分点
如果面试官继续追问,可以补一句:
useRef还经常用来解决闭包旧值问题,因为 ref 对象本身是稳定的,但ref.current可以手动更新成最新值。
一个常见反例
const countRef = useRef(0)
countRef.current += 1
这不会让界面自动刷新,所以如果这个值变化后用户应该看到 UI 改变,就不该用 ref,而应该用 state。
最短记忆方式
ref 记值不刷新,state 变了会重渲染。