React useRef 实战:DOM 引用、可变值缓存与规避闭包陈旧值
test12026-10-085 次阅读
useRef 到底解决了什么问题
useState 存的值每次更新都会触发组件重渲染,而有些值我们只是想“记下来”——比如某个 DOM 节点、定时器的句柄、上一次渲染时的 props。useRef 返回的对象在整个组件生命周期里保持同一个引用,读写 .current 不会引发重渲染,这正是它和 useState 的本质区别。
1 引用 DOM 做命令式操作
最常见的用法是把 ref 挂到原生元素上,拿到真实节点后做聚焦、测量、滚动等命令式操作。
import { useEffect, useRef } from "react";
export function AutoFocusInput() {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
return <input ref={inputRef} placeholder="自动聚焦" />;
}
2 用 ref 保存“上一次”的值
React 不提供内置的“上一次值”钩子,用 useRef 镜像上一次渲染结果即可实现 usePrevious。
import { useEffect, useRef } from "react";
export function usePrevious<T>(value: T): T | undefined {
const ref = useRef<T | undefined>(undefined);
useEffect(() => {
ref.current = value;
});
return ref.current;
}
3 规避闭包里的陈旧值
在 setInterval 或事件回调里直接读 state,拿到的是定义函数那一刻的快照。把它同步进 ref,回调里改读 ref.current 就能拿到最新值。
import { useEffect, useRef, useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
const countRef = useRef(count);
countRef.current = count; // 每次渲染都同步
useEffect(() => {
const id = setInterval(() => {
// 读 ref 而非 count,避免闭包陈旧值
setCount(countRef.current + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return <div>{count}</div>;
}
4 持有定时器与订阅句柄
把句柄存进 ref,清理函数里统一释放,避免组件卸载后还触发 setState 导致内存泄漏与告警。
export function useInterval(cb: () => void, delay: number) {
const saved = useRef(cb);
saved.current = cb;
useEffect(() => {
const id = setInterval(() => saved.current(), delay);
return () => clearInterval(id);
}, [delay]);
}
什么时候不该用 useRef
- 值的变化需要反映到界面上,用 useState。
- 跨组件共享状态,用 Context 或状态库,而不是把 ref 层层透传。
- 需要触发副作用响应变化,用 useEffect 依赖,而不是在 ref 上手动比较。
T
test1
文章作者
useRef 到底解决了什么问题 useState 存的值每次更新都会触发组件重渲染,而有些值我们只是想“记下来”——...
- 分类
- 技术
- 发布时间
- 2026-10-08
- 字数
- 约 1783 字
- 阅读
- 5 次
