useRef 都有哪些使用场景?
分类: 编码创建于: 5/31/2025
useRef
在 React 中的主要使用场景
📌 1. 访问 DOM 元素
最常见的用法,用于直接操作 DOM 节点(如聚焦输入框、测量元素尺寸):
1function TextInput() { 2 const inputRef = useRef(null); 3 4 const focusInput = () => { 5 inputRef.current.focus(); // 手动聚焦输入框 6 }; 7 8 return ( 9 <> 10 <input ref={inputRef} type="text" /> 11 <button onClick={focusInput}>聚焦输入框</button> 12 </> 13 ); 14}
⏱️ 2. 存储计时器 ID
保存 setInterval
/setTimeout
的返回值,用于后续清除:
1function Timer() { 2 const timerRef = useRef(null); 3 4 useEffect(() => { 5 timerRef.current = setInterval(() => { 6 console.log('定时器运行中...'); 7 }, 1000); 8 9 return () => clearInterval(timerRef.current); // 组件卸载时清除 10 }, []); 11}
📊 3. 保存前次状态
实现类似类组件中 prevProps
/prevState
的功能:
1function Counter() { 2 const [count, setCount] = useState(0); 3 const prevCountRef = useRef(); 4 5 useEffect(() => { 6 prevCountRef.current = count; // 更新为当前值 7 }); 8 9 return ( 10 <div> 11 当前值: {count}, 12 前次值: {prevCountRef.current} 13 </div> 14 ); 15}
🧮 4. 缓存计算开销大的值
存储无需触发重新渲染的派生数据:
1function ExpensiveComponent({ data }) { 2 const cachedDataRef = useRef(); 3 4 if (!cachedDataRef.current) { 5 // 仅当第一次渲染时计算 6 cachedDataRef.current = heavyComputation(data); 7 } 8 9 return <div>{cachedDataRef.current}</div>; 10}
🎬 5. 存储动画帧 ID
用于 requestAnimationFrame
动画控制:
1function Animation() { 2 const frameRef = useRef(); 3 4 const animate = () => { 5 // 动画逻辑... 6 frameRef.current = requestAnimationFrame(animate); 7 }; 8 9 useEffect(() => { 10 frameRef.current = requestAnimationFrame(animate); 11 return () => cancelAnimationFrame(frameRef.current); 12 }, []); 13}
📸 6. 保存第三方库实例
存储非 React 管理的对象实例(如 D3 图表、地图库):
1function Chart({ data }) { 2 const chartRef = useRef(null); 3 const chartInstance = useRef(); 4 5 useEffect(() => { 6 if (chartRef.current) { 7 // 初始化图表 8 chartInstance.current = new ThirdPartyChart(chartRef.current, data); 9 } 10 11 return () => chartInstance.current.destroy(); // 清理 12 }, [data]); 13 14 return <div ref={chartRef} />; 15}
🪢 7. 记录组件挂载状态
检测组件是否仍在挂载状态(避免更新卸载组件的状态):
1function DataFetcher() { 2 const isMountedRef = useRef(true); 3 4 useEffect(() => { 5 fetchData().then(data => { 6 if (isMountedRef.current) { 7 // 仅当组件挂载时更新状态 8 setData(data); 9 } 10 }); 11 12 return () => { 13 isMountedRef.current = false; // 卸载时标记 14 }; 15 }, []); 16}
🧩 8. 控制自定义 Hook 内部状态
在自定义 Hook 中维护私有状态:
1function usePrevious(value) { 2 const ref = useRef(); 3 useEffect(() => { 4 ref.current = value; // 更新前值但不触发渲染 5 }, [value]); 6 return ref.current; // 返回前次值 7}
💡 核心特性总结
特性 | 说明 |
---|---|
跨渲染周期持久化 | 存储的值在组件重新渲染时保持不变 |
修改不触发重渲染 | 更改 .current 值不会导致组件更新 |
类似实例变量 | 类似类组件的实例字段(非状态数据) |
同步更新 | 修改后立即可读取新值(不同于 useState 的异步批量更新) |
⚠️ 注意事项
- 不要用于渲染数据:修改
ref
不会触发重渲染 - 初始化避免函数调用:
useRef(computeExpensiveValue())
会在每次渲染执行,应改用:1const ref = useRef(null); 2if (ref.current === null) { 3 ref.current = computeExpensiveValue(); // 惰性初始化 4}
- 严格模式问题:开发环境下双重渲染可能导致
ref
被重置(使用useEffect
可避免)
当需要操作 DOM 或存储与渲染无关的可变值时优先考虑
useRef
,需要触发视图更新则使用useState
。