React `useRef` 常用场景总结
·
React useRef 常用场景总结
useRef 是 React 提供的一个 Hook,用于在函数组件中持有一个 可变的引用对象。
它的特点是:
.current属性可以存储任意可变值。- 修改
.current不会触发组件重新渲染。 - 常用于 DOM 引用、保存变量、缓存值 等场景。
1. 获取和操作 DOM 节点
在 React 中,推荐通过 ref 获取 DOM 元素,而不是 document.querySelector。
import { useRef, useEffect } from "react";
export default function InputFocus() {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
// 组件挂载后自动聚焦
inputRef.current?.focus();
}, []);
return <input ref={inputRef} placeholder="自动聚焦输入框" />;
}
✅ 典型场景:表单聚焦、滚动定位、播放视频等。
2. 保存跨渲染周期的变量
useRef 可以保存一些数据,而不需要因为依赖 useState 而导致组件重复渲染。
import { useRef } from "react";
export default function Timer() {
const countRef = useRef(0);
const handleClick = () => {
countRef.current += 1;
console.log("点击次数:", countRef.current);
};
return <button onClick={handleClick}>点我</button>;
}
✅ 适合存储:定时器 ID、上一次的值、函数调用次数等。
3. 保存定时器 / 事件订阅等引用
import { useRef, useEffect } from "react";
export default function Countdown() {
const timerRef = useRef<number>();
useEffect(() => {
timerRef.current = window.setInterval(() => {
console.log("倒计时中...");
}, 1000);
return () => {
// 清理定时器
clearInterval(timerRef.current);
};
}, []);
return <div>倒计时示例</div>;
}
✅ 场景:保存 setInterval / setTimeout,避免重复绑定。
4. 缓存前一次渲染的值
通过 useRef 可以追踪 上一次的 props 或 state。
import { useState, useEffect, useRef } from "react";
export default function PrevValueDemo() {
const [count, setCount] = useState(0);
const prevCount = useRef<number>();
useEffect(() => {
prevCount.current = count;
}, [count]);
return (
<div>
<p>当前值: {count}</p>
<p>上一次的值: {prevCount.current}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
</div>
);
}
✅ 场景:对比前后 state/props,做差异化处理。
5. 避免闭包陷阱(保存最新的函数或对象)
在 React 中,闭包陷阱经常出现在 定时器、事件回调、订阅 等场景:
- 如果直接在回调里使用 state,可能拿到的是“旧值”。
- 可以用
useRef保存最新的函数或对象,避免闭包导致的“过期数据”。
示例一:保存最新函数
import { useState, useEffect, useRef } from "react";
export default function ClosureTrap() {
const [count, setCount] = useState(0);
const callbackRef = useRef<() => void>();
useEffect(() => {
callbackRef.current = () => {
console.log("最新的 count:", count);
};
}, [count]);
useEffect(() => {
const id = setInterval(() => {
callbackRef.current?.();
}, 1000);
return () => clearInterval(id);
}, []);
return (
<button onClick={() => setCount(count + 1)}>
增加: {count}
</button>
);
}
示例二:保存实例化对象(避免重复创建)
有些场景需要创建一个对象(例如 WebSocket、音频处理器、类实例等),如果放在组件函数体里会在每次渲染时重新实例化。
用 useRef 可以保证实例只被创建一次,并且始终保持引用最新:
import { useRef, useEffect } from "react";
class MyService {
connect() {
console.log("服务已连接");
}
}
export default function ServiceDemo() {
const serviceRef = useRef<MyService>();
if (!serviceRef.current) {
serviceRef.current = new MyService();
}
useEffect(() => {
serviceRef.current?.connect();
}, []);
return <div>服务初始化完成</div>;
}
✅ 典型应用:
- 保存
WebSocket连接对象 - 保存
AudioContext、MediaRecorder等实例 - 保存第三方库(如 Chart.js、Three.js)的实例化对象
6. 转发 Ref(forwardRef)
在自定义组件中转发 ref,以便父组件可以直接获取子组件的 DOM。
import { forwardRef, useRef, useImperativeHandle } from "react";
const MyInput = forwardRef((props, ref) => {
const inputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus()
}));
return <input ref={inputRef} {...props} />;
});
export default function Parent() {
const ref = useRef<{ focus: () => void }>(null);
return (
<div>
<MyInput ref={ref} />
<button onClick={() => ref.current?.focus()}>聚焦输入框</button>
</div>
);
}
✅ 场景:组件封装时,提供对内部 DOM 的操作能力。
总结
useRef 的常用场景可以归纳为:
- 获取 DOM 节点(替代
document.querySelector) - 保存变量(不会触发渲染)
- 保存定时器 / 事件订阅引用
- 保存上一次的值
- 避免闭包陷阱(保持函数或对象引用最新)
- 转发 Ref 给子组件
简单一句话:
useRef= 可变的盒子,存储数据但不触发渲染。
更多推荐


所有评论(0)