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. 将 ref 附加到 DOM 元素 */}
); } ​ 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 组件。​ ​ 本文暂未被其它文档引用