React - 使用 useRef Hook
React 会随着组件状态的变化自动更新 HTML 元素。这大大简化了 UI 开发,只需更新组件的状态即可。传统上,直接访问 DOM 元素来更新组件的 UI 是常见的做法。
有时我们可能需要回退到直接访问 DOM 元素并更新组件的 UI。React ref 在这种情况下提供了帮助。它提供了对 DOM 元素的直接访问。同时,它确保组件与 React Virtual DOM 和 HTML DOM 无缝协作。
React 提供了一个函数 createRef,用于在 class 组件中创建 ref。在 function component 中的对应功能是 useRef hook。本章将介绍如何使用 useRef。
useRef hook 的签名
useRef 的目的是返回一个可变对象,该对象会在重新渲染之间持久存在。useRef 的签名如下 −
<refObj> = useRef(<val>)
其中,
val 是为返回的可变对象 refObj 设置的初始值。
refObj 是 hook 返回的对象。
要自动将 DOM 对象附加到 refObj,应将其设置在元素的 ref 属性中,如下所示 −
<input ref={refObj} />
要访问附加的 DOM 元素,使用 refObj 的 current 属性,如下所示 −
const refElement = refObj.current
应用 ref hook
在本章中,我们通过创建一个 React 应用来学习如何应用 useRef。
首先,使用以下命令创建一个新的 React 应用并启动它。
create-react-app myapp cd myapp npm start
接下来,在组件文件夹(src/components/RefInput.js)下创建一个 React 组件 RefInput。
function RefInput() {
return <div>Hello World</div>
}
export default RefInput
接下来,更新根组件(App.js)以使用我们的新组件。
import RefInput from "./components/RefInput";
function App() {
return (
<div style={{ padding: "5px"}}>
<RefInput />
</div>
);
}
export default App;
接下来,为 RefInput 组件添加计数器功能,如下所示 −
import {useState} from 'react'
function RefInput() {
const [count, setCount] = useState(0)
const handleClick = () => setCount(count + 1)
return (
<div>
<div>Counter: {count} <button onClick={handleClick}>+</button></div>
</div>
)
}
export default RefInput
这里我们有:
使用 useState hook 来处理计数器状态变量(count)。
在 JSX 中渲染计数器状态变量。
添加了一个按钮并绑定了点击事件处理器(handleClick),它将使用 setCount 方法递增计数器。
接下来,添加一个输入字段,并根据用户在输入字段中输入的值显示问候消息,如下所示 −
import {useState, useRef} from 'react'
function RefInput() {
const [count, setCount] = useState(0)
const inputRef = useRef(null)
const labelRef = useRef(null)
console.log("RefInput is (re)rendered")
const handleClick = () => setCount(count + 1)
const handleChange = () => labelRef.current.innerText = inputRef.current.
value == "" ? "World" : inputRef.current.value
return (
<div>
<div>Counter: {count} <button onClick={handleClick}>+</button></div>
<div style={{ paddingTop: "5px"}}>
<label>Enter your name: </label><input type="text" name="username"
ref={inputRef} onChange={handleChange}/>
<br />
<div>Hello, <span ref={labelRef}></span></div>
</div>
</div>
)
}
export default RefInput
这里我们有 −
创建了一个 ref,inputRef 来表示 input 元素,并通过 ref 属性将其附加到相关元素上。
创建了另一个 ref,labelRef 来表示问候消息元素,并通过 ref 属性将其附加到相关元素上。
为 input 元素附加了一个事件处理器 handleChange。该事件处理器使用 inputRef ref 获取问候消息,并使用 labelRef ref 更新消息。
接下来,在浏览器中打开应用并输入您的姓名。应用将更新问候消息,如下所示。
检查您的控制台,您会注意到组件没有重新渲染。因为 React 仅在状态变化时重新渲染,而 ref 不会引起任何状态变化,因此组件没有重新渲染。
接下来,点击 + 按钮。它将通过重新渲染组件来更新计数器,因为状态(count)发生了变化。如果您仔细观察,您会发现消息保持不变。这种行为的原因是 React 在渲染之间保留了 ref 值。
useRef 的使用场景
useRef 的部分使用场景如下 −
访问 JavaScript DOM API − JavaScript DOM API 提供了丰富的功能集来操作应用程序的 UI。当应用程序功能需要访问 JavaScript DOM API 时,可以使用 useRef 来获取原始 DOM 对象。一旦获取了原始 DOM 对象,应用程序就可以使用 DOM API 来访问所有功能。DOM API 的部分示例如下 −
聚焦输入元素
选择文本
使用 media playback API 播放音频或视频
命令式动画 − Web Animation API 通过命令式编程而非声明式编程提供了丰富的动画功能。要使用 Web Animation API,我们需要访问原始 DOM。
与第三方库集成 − 由于第三方库需要访问原始 DOM 来实现其功能,因此必须使用 useRef 从 React 获取 DOM 引用并提供给第三方库。
总结
尽管 React 开发 UI 的方式简单易用,但在某些特定场景下,基于 DOM API 开发 UI 也有其自身优势。useRef hook 在这些场景中完美契合,并提供简单干净的 API 来直接访问 DOM 元素及其 API。