在这篇文章中,您将学习最容易被误解和误用的钩子之一,您将学习如何使用useRef() 钩子来创建引用并使用它们来访问 DOM 元素。
useRef返回一个可变引用对象,其中包含一个名为current 的属性字段,该字段设置为传递的参数值。返回的对象将在组件的整个生命周期内保留。
1 - 语法
useRef(initialValue) 是一个内置的 React 钩子,它接受一个参数作为初始值并返回一个引用。引用是具有单个属性“current”的对象,可以访问和更改
// Creating a reference an providing an initial value
const reference = useRef(initialValue);
const handler = () => {
// Accessing the reference value
const referenceValue = reference.current;
// Updating reference value
reference.current = reference.current + 1;
};
2 — 基本用法
让我们想象一下,无论出于何种原因,您想计算更改输入字段中的值时组件 重新渲染的次数
2 — 1 使用 useState 钩子计算渲染(错误的方式)
export default function RenderCounter() {
const [userInput, setuserInput] = useState("");
const [renders, setRenders] = useState(0);
useEffect(() => {
setRenders(renders + 1);
});
return (
<>
<input
valuue={userInput}
onChange={(event) => setuserInput(event.target.value)}
/>
<p>The componrt rendered {renders} times</p>
</>
);
}
这不是正确的方法,因为当应用程序重新渲染时,渲染值会发生变化,这再次导致应用程序重新渲染,从而形成无限循环。
2 — 2 使用 useRef 钩子计算渲染(正确的方法)
export default function RenderCounter() {
const [userInput, setuserInput] = useState("");
const renders = useRef(1);
useEffect(() => {
renders.current = renders.current+1;
});
return (
<>
<input
valuue={userInput}
onChange={(event) => setuserInput(event.target.value)}
/>
<p>The componrt rendered {renders.current} times</p>
</>
);
}
通过这种方式,您可以看到,每次您通过键入内容更改状态时,应用都会重新渲染,但更改参考值本身不会触发重新渲染。
3 - 引用和状态的区别
让我们更深入地看看状态和引用之间的区别,我们将实现一个 组件,该组件在每次渲染时向控制台打印一条消息“组件已渲染”,并显示另一条消息显示按钮被点击了多少次.
首先让我们使用useState() 来实现ButtonCounter:
export default function ButtonCounter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("the component have been rendered");
});
const handler = () => {
const updatedCount = count + 1;
setCount(updatedCount);
console.log(`Clicked ${updatedCount} times`);
};
return <button onClick={handler}>Click me</button>;
}
请注意,每次更新状态时,组件都会重新渲染。
现在让我们使用useRef() 钩子重新实现我们的组件:
export default function ButtonCounter() {
const count = useRef(1);
const handler = () => {
const updatedCount = count.current + 1;
count.current = updatedCount;
console.log(`Clicked ${updatedCount} times`);
};
useEffect(() => {
console.log("the component have been rendered");
});
return <button onClick={handler}>Click me</button>;
}
请注意,这次组件仅呈现一次,但每次单击时计数值都会发生变化。
总之
- useRef用于在组件的整个生命周期内存储持久可变对象。
- useRef在改变 .current值时不会触发重新渲染。
- 引用更新是同步的,而状态更新是异步的并触发重新渲染。
4 - 看一个更具体的用例
useRef最常用于引用 dom 元素。所有 JSX 元素都可以有一个内置的 ref 属性,我们可以使用它来引用 dom 元素。(使用 vanilla JavaScript,我们将不得不使用 document.querySelector() 等)。
例如,您需要在组件挂载时关注输入字段。要使其工作,您需要创建对输入的引用,将引用分配给输入的 ref 属性,一旦组件安装调用元素上的element.focus() 方法。
export default function TextInputWithFocus() {
const inputEl = useRef(null);
useEffect(() => {
// `current` points to the mounted text input element
inputEl.current.focus();
}, []);
return (
<>
<input ref={inputEl} type='text' />
</>
);
}
在初始渲染期间,React 仍然确定组件的输出是什么,从而将 inputEl.current 评估为 undefined。useEffect(callback, []) 的回调函数是访问 inputRef.current 的正确位置,因为它在挂载后立即调用回调。
5. 结论
使用 useRef() 返回一个引用对象。引用对象有一个名为 current 的属性,您可以使用此属性来读取或更新引用值。
引用的值在组件重新渲染之间是持久的。
与更新状态不同,改变引用不会触发重新渲染。
当试图访问 DOM 元素时,refs 可以派上用场。将引用分配给元素的 ref 属性,以便可以使用引用访问它。