useRef
是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。
const refContainer = useRef(initialValue);
initialValue
: ref 对象的初始值(.current
属性的初始值).current
属性被初始化为传入的参数.current
属性不会导致组件重新渲染最常见的用法是访问 JSX 渲染的 DOM 元素:
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
inputEl.current.focus();
};
return (
<>
>
);
}
可以存储任何可变值,类似于类组件中的实例属性:
function Timer() {
const intervalRef = useRef();
useEffect(() => {
intervalRef.current = setInterval(() => {
console.log('Timer tick');
}, 1000);
return () => clearInterval(intervalRef.current);
}, []);
// ...
}
实现获取上一次 props 或 state 的功能:
function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
});
const prevCount = prevCountRef.current;
return (
Current: {count}, Previous: {prevCount}
);
}
解决 React 18+ 严格模式下 useEffect 执行两次的问题:
useEffect(() => {
const executedRef = useRef(false);
if (!executedRef.current) {
executedRef.current = true;
// 你的初始化代码
}
}, []);
将 ref 传递给子组件:
const FancyInput = React.forwardRef((props, ref) => {
return ;
});
function App() {
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
}, []);
return ;
}
动态设置多个 ref:
function MeasureExample() {
const [height, setHeight] = useState(0);
const measuredRef = useCallback(node => {
if (node !== null) {
setHeight(node.getBoundingClientRect().height);
}
}, []);
return (
Hello, world
The above header is {Math.round(height)}px tall
);
}
function Canvas() {
const canvasRef = useRef(null);
useEffect(() => {
const ctx = canvasRef.current.getContext('2d');
// 使用第三方库绘制
new ThirdPartyLibrary(ctx);
}, []);
return ;
}
特性 | useRef | useState |
---|---|---|
触发重新渲染 | 否 | 是 |
值更新时机 | 同步 | 异步 |
适合存储 | DOM 引用、可变变量、计时器 | 需要触发 UI 更新的状态 |
访问方式 | .current 属性 |
直接访问状态变量 |
初始化 | 参数作为 .current 初始值 |
参数作为初始状态 |
不要在渲染期间写入/读取 ref.current
:
// 错误示例
function MyComponent() {
const myRef = useRef();
myRef.current = 123; // 不应该在渲染期间修改
return {myRef.current}; // 也不应该依赖渲染期间的值
}
ref 不会自动通知内容变化:
多个 refs 合并:
function useCombinedRefs(...refs) {
return useCallback(el => {
refs.forEach(ref => {
if (!ref) return;
if (typeof ref === 'function') ref(el);
else ref.current = el;
});
}, refs);
}
服务端渲染(SSR)注意事项:
useRef
本身是轻量级的,但以下情况需要注意:
避免在渲染函数中创建新 ref:
// 不好 - 每次渲染都创建新 ref
function Component() {
return ;
}
// 好 - ref 只创建一次
function Component() {
const ref = useRef();
return ;
}
大量 ref 的内存问题:
useRef
是 React Hooks 中一个非常实用的工具,它: