React 中 useRef
Hook 作用及实例
useRef
是 React 中非常常用的一个 Hook,它的主要作用是持有对某个 DOM 元素或值的引用,并且可以在组件的整个生命周期中保持其不变的引用。与 useState
不同,useRef
主要用于保存不需要引起组件重渲染的引用。本文将详细介绍 useRef
的作用、使用方法、以及在 React 项目中如何有效利用 useRef
来提升开发效率。
1. useRef
基础介绍
useRef
是 React 16.8 引入的 Hook,用来创建一个可变的引用对象。引用对象的值在组件的重渲染过程中保持不变,意味着它可以存储一个持续的值而不会触发组件的重新渲染。这使得 useRef
成为一个适用于多种场景的工具,尤其是在需要直接访问 DOM 元素或者保存任何需要跨渲染周期保持的值时。
useRef
返回一个对象,该对象有一个名为 current
的属性,current
属性可以用来保存任何类型的值。
jsxCopy Codeimport { useRef } from 'react';
function Example() {
const inputRef = useRef(null);
const focusInput = () => {
// 使用 useRef 获取对 DOM 元素的引用
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} />
<button onClick={focusInput}>Focus the input</button>
</div>
);
}
在上面的例子中,useRef
被用来创建一个对 input
元素的引用。当按钮被点击时,我们通过 inputRef.current.focus()
来触发对 input
元素的聚焦,而不会引发组件的重渲染。
2. useRef
的常见用途
2.1 访问 DOM 元素
最常见的 useRef
用法就是访问 DOM 元素,尤其是在你需要手动操控 DOM 时(如聚焦、滚动等操作)。
jsxCopy Codeimport React, { useRef } from 'react';
const FocusableInput = () => {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus(); // 聚焦输入框
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus the Input</button>
</div>
);
};
export default FocusableInput;
2.2 保持跨渲染周期的数据
useRef
不会引起组件重新渲染,因此它可以用于存储跨渲染周期的数据。例如,你可以用它来存储计时器 ID、定时器函数、上一次渲染的时间戳等信息。
jsxCopy Codeimport { useEffect, useRef } from 'react';
function Timer() {
const timerRef = useRef(null);
useEffect(() => {
timerRef.current = setInterval(() => {
console.log('Timer is running');
}, 1000);
return () => {
clearInterval(timerRef.current); // 清除定时器
};
}, []);
return <div>Timer is running. Check console for output.</div>;
}
在这个例子中,useRef
用来保存定时器的 ID。这样我们可以确保每次定时器清除时都能正确地使用它,而不会因为组件重渲染而丢失。
2.3 存储前一个状态的值
有时候,你可能想要在组件中保存某个值的“前一个值”,这种情况下 useRef
也非常适用。
jsxCopy Codeimport React, { useState, useEffect, useRef } from 'react';
function PreviousValue() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count; // 更新前一个 count 的值
}, [count]);
return (
<div>
<h1>Current count: {count}</h1>
<h2>Previous count: {prevCountRef.current}</h2>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default PreviousValue;
在这个例子中,useRef
用来存储 count
的前一个值。通过 useEffect
,每次 count
更新时,我们都会将当前的值保存在 prevCountRef.current
中,这样就可以在下一次渲染时访问到上一个 count
的值。
2.4 作为缓存值
如果你需要存储一个数据,但又不希望每次更新时都重新计算或获取这个数据,可以使用 useRef
来缓存它。例如,下面的代码缓存了计算结果,避免了重复计算。
jsxCopy Codeimport React, { useRef, useState } from 'react';
function ExpensiveCalculation() {
const [count, setCount] = useState(0);
const resultRef = useRef();
const expensiveCalculation = (num) => {
console.log('Performing expensive calculation...');
return num * 2; // 假设这是一个昂贵的计算
};
const handleClick = () => {
if (!resultRef.current) {
resultRef.current = expensiveCalculation(count);
}
alert(`Cached result: ${resultRef.current}`);
};
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={handleClick}>Get Cached Result</button>
</div>
);
}
export default ExpensiveCalculation;
在这个例子中,useRef
用来缓存计算结果。如果用户多次点击按钮,expensiveCalculation
函数只会在第一次点击时执行,之后都直接使用缓存的结果。
2.5 用于处理动画
useRef
也常用于存储动画的状态,特别是对于需要引用动画相关 DOM 元素时。由于动画通常需要反复操作 DOM,因此 useRef
是一个理想的工具来避免不必要的组件重渲染。
jsxCopy Codeimport React, { useEffect, useRef } from 'react';
function BouncingBall() {
const ballRef = useRef();
useEffect(() => {
const ball = ballRef.current;
const animate = () => {
ball.style.transform = `translateY(${Math.random() * 100}px)`;
requestAnimationFrame(animate);
};
animate();
return () => {
cancelAnimationFrame(animate);
};
}, []);
return <div ref={ballRef} style={{ width: 50, height: 50, backgroundColor: 'red', borderRadius: '50%' }} />;
}
export default BouncingBall;
这个例子展示了如何使用 useRef
来存储 DOM 元素,并通过 requestAnimationFrame
来实现平滑的动画效果。
3. useRef
与 useState
对比
虽然 useRef
和 useState
都可以用来存储值,但是它们的工作方式和应用场景有所不同:
特性 | useRef |
useState |
---|---|---|
引发重新渲染 | 不会引发重新渲染 | 会引发重新渲染 |
用途 | 存储对 DOM 元素的引用,保存不需要渲染的数据 | 存储需要在组件中反映的可变数据 |
持久性 | 跨重渲染周期持久化存储数据 | 每次渲染时都会重新计算 |
示例 | 用于获取 DOM 元素,缓存值 | 用于存储 UI 状态,管理表单数据 |
总结来说,useRef
更适用于存储那些不需要引起 UI 更新的值,而 useState
适用于需要引发 UI 更新的状态。
4. 使用场景
4.1 表单管理
在表单中,我们常常需要通过 useRef
来直接引用某些元素,例如 input
,并操作其聚焦、选中文本等。
jsxCopy Codeimport React, { useRef } from 'react';
function Form() {
const nameRef = useRef();
const ageRef = useRef();
const handleSubmit = (event) => {
event.preventDefault();
console.log('Name:', nameRef.current.value);
console.log('Age:', ageRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<input ref={nameRef} type="text" placeholder="Enter name" />
<input ref={ageRef} type="number" placeholder="Enter age" />
<button type="submit">Submit</button>
</form>
);
}
export default Form;
这个例子展示了如何使用 useRef
来管理表单