5.useRef 有哪些常见的应用场景? 副本
来源:https://my.feishu.cn/docx/EbAYdqh7goAZqXxhN8Ocys2onNe
采集状态:已完成正文采集;已滚动到底部并按块及代码行号采集
useRef 是 React Hooks 中一个独特且功能强大的工具。与 useState 不同,**更新 useRef 的值不会触发组件的重新渲染**。这个核心特性决定了它的主要用途:在需要一个“可变容器”来跨渲染周期持久化数据,但又不希望这个数据的变化影响视图时,useRef 就成了最佳选择。
本质上,我们可以把 useRef 看作是函数组件实例上的一个“储物箱”,我们可以随时存取物品,而不用每次都重新整理(渲染)整个房间。
下面我们来探讨几个最常见的应用场景。
1. 访问和操作 DOM 元素
这是 useRef 最广为人知的用途。在 React 的声明式世界里,我们通常不直接操作 DOM。但总有一些场景需要我们“破例”,例如:
•
自动聚焦输入框
•
获取元素的尺寸或位置
•
控制视频或音频的播放
•
集成需要直接访问 DOM 的第三方库(如 D3.js)
在这些情况下,useRef 提供了一个可靠的桥梁。
工作流程:
1.
创建一个 ref 对象:const inputRef = useRef(null);
2.
将其附加到 JSX 元素的 ref 属性上:
3.
在 useEffect 或事件处理函数中,通过 inputRef.current 访问该 DOM 节点。
示例:页面加载后自动聚焦输入框
import React, { useRef, useEffect } from 'react';
function AutoFocusInput() {
// 1. 创建一个 ref
const inputRef = useRef(null);
useEffect(() => {
// 3. 在 effect 中访问 DOM 节点
// inputRef.current 指向的就是真实的 DOM 元素
if (inputRef.current) {
inputRef.current.focus();
}
}, []); // 空依赖数组确保 effect 仅在组件挂载时运行一次
return (
);
}
2. 存储跨渲染周期的可变值
这是 useRef 更为精妙的用法。当我们有一个值,需要在多次渲染之间保持不变,但它的变化又不应该引起视图更新时,useRef 就非常适用。最典型的例子是管理定时器。
场景:管理 setTimeout 或 setInterval
如果我们将定时器 ID 存储在 useState 中,每次 setTimerId 都会导致不必要的重渲染。而使用 useRef,我们可以悄无声息地在后台存储和清除定时器。
示例:一个简单的秒表
import React, { useState, useRef } from 'react';
function Stopwatch() {
const [seconds, setSeconds] = useState(0);
// 使用 ref 来存储 interval ID
const intervalRef = useRef(null);
const handleStart = () => {
// 防止重复启动
if (intervalRef.current) return;
intervalRef.current = setInterval(() => {
// 注意:这里使用函数式更新,避免依赖外部的 `seconds` 变量
setSeconds(prevSeconds => prevSeconds + 1);
}, 1000);
};
const handleStop = () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null; // 清理 ref
}
};
const handleReset = () => {
handleStop();
setSeconds(0);
};
return (
秒表: {seconds}s
);
}
在这个例子中,intervalRef.current 的变化(从 null 变为一个数字 ID,再变回 null)完全独立于组件的渲染流程,实现了高效的状态管理。
3. 缓存上一次的 Props 或 State
有时我们需要在渲染时拿到上一次的某个值,以便进行比较或计算。例如,我们想知道一个数字是增大了还是减小了。useRef 和 useEffect 的组合可以优雅地实现这个需求。
工作流程:
1.
用 useRef 创建一个 ref 来存储上一次的值。
2.
在 useEffect 中,**先执行逻辑**(此时 ref 中还是上一次的值),**然后更新 ref** 为当前值,为下一次渲染做准备。
示例:显示当前值和上一个值
import React, { useState, useEffect, useRef } from 'react';
function PreviousValueDisplay({ value }) {
// 用 ref 存储上一次的 value
const prevValueRef = useRef();
useEffect(() => {
// effect 会在每次渲染完成后运行
// 所以在这次更新 ref 之前,它存储的还是上一次渲染的值
prevValueRef.current = value;
}, [value]); // 依赖 value,确保 value 变化时更新 ref
// 在渲染时,prevValueRef.current 还是上一次的值
const prevValue = prevValueRef.current;
return (
当前值: {value}, 上一个值: {prevValue === undefined ? 'N/A' : prevValue}
);
}
function App() {
const [count, setCount] = useState(0);
return (
);
}
这个模式非常实用,可以看作是实现了一个自定义 Hook usePrevious 的基础。
useState vs useRef:如何选择?
为了更好地理解 useRef,我们可以建立一个清晰的选择标准:
特性
useState
useRef
触发重渲染
是,setState 会触发组件重新渲染。
否,修改 .current 属性不会触发重渲染。
主要用途
管理组件的声明式状态,即那些需要反映在 UI 上的数据。
存储不需要触发 UI 更新的数据,或用于命令式地访问 DOM。
值更新方式
异步更新,通过 setState 函数。
同步更新,直接修改 .current 属性。
何时使用
当数据变化需要让用户看到时。
当你需要一个“后台”变量,或者需要与 React 之外的世界(如 DOM)交互时。
总结
useRef 为我们提供了一个逃生舱,让我们可以在必要时跳出 React 纯粹的声明式数据流。它就像是函数组件的“实例属性”,为我们处理那些与渲染无关的副作用、状态和外部交互提供了强大的支持。
掌握 useRef 的这几个核心场景,可以帮助我们编写出更干净、更高效、也更灵活的 React 组件。
本文暂未被其它文档引用