在 React 中,useRef 是一个内置 Hook,用于创建一个可变的 ref 对象,其 .current 属性可以保存任何值,并且在组件的整个生命周期内保持不变。以下是它的核心用法和适用场景:
一、基本用法
1. 获取 DOM 元素引用
import { useRef } from 'react';
function TextInputWithFocusButton() {
const inputRef = useRef(null);
const handleClick = () => {
inputRef.current.focus();
// 获取 DOM 节点并调用 focus 方法
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={handleClick}>聚焦输入框</button>
</>
);
}
useRef(null)创建一个 ref 对象,初始值为null。- 通过
ref={inputRef}将 ref 绑定到 DOM 元素,inputRef.current会指向该 DOM 节点。
2. 保存可变值(替代 instance variables)
import { useRef, useEffect } from 'react';
function Timer() {
const intervalRef = useRef(null); // 保存定时器 ID
const countRef = useRef(0); // 保存计数状态,不会触发重新渲染
useEffect(() => {
intervalRef.current = setInterval(() => {
countRef.current += 1;
console.log('计数:', countRef.current);
}, 1000);
return () => {
clearInterval(intervalRef.current); // 清理定时器
};
}, []);
return <div>后台计时器运行中...</div>;
}
ref对象的值变化不会触发组件重新渲染,适合存储不需要影响 UI 的数据(如定时器 ID、DOM 节点)。
二、适用场景
1. 操作 DOM
- 聚焦输入框、滚动到特定位置、测量 DOM 尺寸等:
const divRef = useRef(null);
useEffect(() => {
const height = divRef.current.offsetHeight;
console.log('div 高度:', height);
}, []);
return <div ref={divRef}>测量我</div>;
2. 存储不需要触发渲染的变量
- 避免闭包陷阱:在定时器、异步回调中获取最新值:
const latestCount = useRef(count);
// 保存最新的 count 值
useEffect(() => {
latestCount.current = count; // 每次 count 变化时更新
}, [count]);
useEffect(() => {
const interval = setInterval(() => {
console.log('最新 count:', latestCount.current); // 总是获取最新值
}, 1000);
return () => clearInterval(interval);
}, []);
3. 跨渲染周期保存值
- 在组件多次渲染之间保持数据:
function App() {
const prevValue = useRef(null); // 保存上一次的 props 或 state
useEffect(() => {
prevValue.current = props.value; // 组件渲染后更新
});
return <div>之前的值: {prevValue.current}</div>;
}
4. 优化性能(缓存复杂对象)
- 使用
useRef缓存昂贵的计算结果,避免重复计算:
const cachedValue = useRef(null);
if (!cachedValue.current) {
cachedValue.current = expensiveCalculation(); // 只计算一次
}
三、注意事项
- 不要在渲染过程中修改
ref.current渲染期间修改 ref 可能导致意外行为,应在事件处理函数或副作用中修改。 ref更新不会触发渲染 与state不同,修改ref.current不会导致组件重新渲染。- 避免过度使用 优先使用
state管理影响 UI 的数据,仅在必要时使用ref(如操作 DOM、存储临时值)。
四、对比其他 Hook
| Hook | 用途 | 是否触发渲染 |
|---|---|---|
useState | 管理状态,状态变化触发重新渲染 | ✅ |
useRef | 存储可变值、获取 DOM 引用,不触发渲染 | ❌ |
useCallback | 缓存函数引用,避免不必要的依赖变化 | ❌ |
useMemo | 缓存计算结果,优化性能 | ❌ |
总结
- 何时用:操作 DOM、存储不需要触发渲染的变量、跨渲染周期保存数据。
- 为何用:避免闭包陷阱、优化性能、获取 DOM 节点。
- 注意点:不要在渲染期间修改
ref,ref更新不会触发重渲染。 如果需要在组件间共享 ref,可以结合useImperativeHandle和forwardRef使用。