React 中 useRef 除了引用 DOM 还能做什么?

发布时间:2026/7/30 10:16:41
React 中 useRef 除了引用 DOM 还能做什么? React 中 useRef 除了引用 DOM 还能做什么一句话总结useRef 是一个「渲染间持久化的可变容器」除了存 DOM 节点还能存定时器 ID、上一次的值、跨渲染的标志位是函数组件里不触发重渲染的「私有变量」正文目录useRef 到底是什么5 大高频使用场景 代码示例万能兜底什么时候用 useRef vs useState预防 checklist不再踩坑一句话总结一、useRef 到底是什么const ref useRef(initialValue); // ref.current initialValue初始值 // 修改 ref.current 不会触发重渲染 // 组件每次渲染ref 是同一个对象核心特性特性useRefuseState修改后触发重渲染❌ 不触发✅ 触发跨渲染保持值✅ 是✅ 是修改方式ref.current valsetState(val)适合存什么DOM、定时器、标志位需要展示的 UI 数据一句话需要页面响应变化 → useState只需要存个值但不影响 UI → useRef。二、5 大高频使用场景 代码示例① 引用 DOM 节点最常见import { useRef, useEffect } from react; function InputFocus() { const inputRef useRef(null); useEffect(() { inputRef.current?.focus(); // ✅ 挂载后自动聚焦 }, []); return input ref{inputRef} /; }② 存储定时器 ID避免泄漏// ❌ 定时器存在普通变量里每次渲染都会重置 function Timer() { let timerId null; // ❌ 每次渲染都是 null useEffect(() { timerId setInterval(() console.log(tick), 1000); return () clearInterval(timerId); // ❌ timerId 可能是 null }, []); }修复用 useRef 存定时器// ✅ useRef 存定时器 ID跨渲染稳定 import { useRef, useEffect } from react; function Timer() { const timerRef useRef(null); useEffect(() { timerRef.current setInterval(() { console.log(tick); }, 1000); return () clearInterval(timerRef.current); // ✅ 清理时能拿到 }, []); const stop () clearInterval(timerRef.current); return button onClick{stop}停止/button; }③ 记录上一次的值prevValue// ✅ 用 useRef 记录上一次的 props/state import { useRef, useEffect } from react; function PriceDisplay({ price }) { const prevPriceRef useRef(price); useEffect(() { prevPriceRef.current price; // ✅ 渲染后更新 }); const isUp price prevPriceRef.current; return ( div style{{ color: isUp ? red : green }} ¥{price} {isUp ? ↑ : ↓} /div ); }④ 防止 useEffect 里的闭包陷阱// ❌ useEffect 空依赖但里面用了最新的 state → 闭包旧值 function Counter() { const [count, setCount] useState(0); useEffect(() { const timer setInterval(() { console.log(count); // ❌ 永远是 0 setCount(count 1); // ❌ 永远设成 1 }, 1000); return () clearInterval(timer); }, []); }修复方案一useRef 存最新值// ✅ useRef 存最新 count定时器里读 ref.current function Counter() { const [count, setCount] useState(0); const countRef useRef(count); useEffect(() { countRef.current count; // ✅ 每次渲染更新 ref }); useEffect(() { const timer setInterval(() { setCount(countRef.current 1); // ✅ 读最新值 }, 1000); return () clearInterval(timer); }, []); return div{count}/div; }修复方案二函数式更新更简洁// ✅ 函数式更新不需要 ref function Counter() { const [count, setCount] useState(0); useEffect(() { const timer setInterval(() { setCount(prev prev 1); // ✅ 拿到最新值 }, 1000); return () clearInterval(timer); }, []); return div{count}/div; }⑤ 标志位控制「只执行一次」或「是否已初始化」// ✅ 用 useRef 做初始化标志防止重复执行 import { useRef, useEffect } from react; function useInitOnce(callback) { const initializedRef useRef(false); useEffect(() { if (initializedRef.current) return; // ✅ 只执行一次 initializedRef.current true; callback(); }, [callback]); }// ✅ 防抖用 useRef 存防抖定时器 function SearchInput() { const [value, setValue] useState(); const debounceRef useRef(null); const handleChange (e) { const val e.target.value; setValue(val); clearTimeout(debounceRef.current); // ✅ 清掉上一个定时器 debounceRef.current setTimeout(() { fetchResults(val); // ✅ 500ms 后发请求 }, 500); }; useEffect(() { return () clearTimeout(debounceRef.current); // ✅ 卸载时清理 }, []); return input value{value} onChange{handleChange} /; }三、万能兜底什么时候用 useRef vs useState决策流程这个值变化后UI 需要更新吗 ├── 是 → useState └── 否 → 这个值需要在事件/定时器里访问吗 ├── 是 → useRef └── 否 → 普通变量就行场景用什么原因输入框内容useStateUI 需要响应加载状态useStateUI 需要响应DOM 节点引用useRef不需要触发渲染定时器 IDuseRef不需要触发渲染上一次的值useRef用于比较不直接渲染防抖定时器useRef不需要触发渲染初始化标志useRef只控制逻辑不渲染isMounted 标志useRef防止卸载后 setState万能模板import { useState, useRef, useEffect } from react; function useDebounce(callback, delay) { const timerRef useRef(null); // ✅ 存定时器 const callbackRef useRef(callback); // ✅ 存最新 callback useEffect(() { callbackRef.current callback; // ✅ 每次渲染更新 }); useEffect(() { return () clearTimeout(timerRef.current); // ✅ 卸载清理 }, []); const debounced (...args) { clearTimeout(timerRef.current); timerRef.current setTimeout(() { callbackRef.current(...args); // ✅ 调最新的 }, delay); }; return debounced; }四、预防 checklist需要 UI 响应 → useState不需要 → useRef定时器、事件监听器的 ID用 useRef 存useEffect cleanup 里清理防抖/节流用 useRef 存定时器别用普通变量每次渲染重置闭包陷阱优先用函数式更新解决实在不行再上 useRef记录「上一次的值」用 useRef useEffect在渲染后更新不要在渲染过程中写 ref.current除初始化只在事件回调和 effect 里写ref.current 读写在事件处理和 effect 里是安全的不要在组件渲染阶段依赖它useRef 初始值useRef(null)给 DOMuseRef(0)给计数器useRef(false)给标志位五、一句话总结「useRef 不触发重渲染的持久化容器」。DOM 引用只是最基础的用法定时器 ID、上一次的值、初始化标志、防抖定时器都是它的主场。记住UI 要变用 useState逻辑要存不变用 useRef最后问候亲爱的朋友们并邀请你们阅读我的全新著作 《React 进阶实战》

相关新闻