如何使用 useRef() 钩子

2,781 阅读3分钟

在这篇文章中,您将学习最容易被误解和误用的钩子之一,您将学习如何使用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>;
}

请注意,每次更新状态时,组件都会重新渲染。

image.png

现在让我们使用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>;
}

请注意,这次组件仅呈现一次,但每次单击时计数值都会发生变化。

image.png

总之

  1. useRef用于在组件的整个生命周期内存储持久可变对象。
  2. useRef在改变 .current值时不会触发重新渲染。
  3. 引用更新是同步的,而状态更新是异步的并触发重新渲染。

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 属性,以便可以使用引用访问它。