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 Code
import { 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 Code
import 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 Code
import { 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 Code
import 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 Code
import 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 Code
import 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. useRefuseState 对比

虽然 useRefuseState 都可以用来存储值,但是它们的工作方式和应用场景有所不同:

特性 useRef useState
引发重新渲染 不会引发重新渲染 会引发重新渲染
用途 存储对 DOM 元素的引用,保存不需要渲染的数据 存储需要在组件中反映的可变数据
持久性 跨重渲染周期持久化存储数据 每次渲染时都会重新计算
示例 用于获取 DOM 元素,缓存值 用于存储 UI 状态,管理表单数据

总结来说,useRef 更适用于存储那些不需要引起 UI 更新的值,而 useState 适用于需要引发 UI 更新的状态。

4. 使用场景

4.1 表单管理

在表单中,我们常常需要通过 useRef 来直接引用某些元素,例如 input,并操作其聚焦、选中文本等。

jsxCopy Code
import 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 来管理表单