掌握React的Useref Hook:深入潜水
#javascript #react #hooks #useref

react的 useRef 挂钩是一种功能强大且通用的工具,可让您与DOM进行交互,管理状态并优化性能,而不会引起不必要的重新订阅。在本综合指南中,我们将深入了解 useRef 在引擎盖下工作,为什么它不会触发重新订单以及如何利用其全部潜力。

useref 简介

React的功能组件已彻底改变了我们构建用户界面的方式。随着钩子的引入,管理状态和副作用变得更加简单。在这些钩子中, useRef 以其独特的功能而脱颖而出。

什么是 useref

useref是一个钩子中的钩子,可让您创建对dom元素或任何其他呈现在渲染中的其他值的可变引用。虽然它通常用于直接访问和操纵DOM元素,但它也是一个方便的工具,用于存储值时不应在更改时触发重新租赁的值。

为什么有用?

这是useref闪耀的一些常见场景:

  • 访问和操纵DOM元素:使用 useRef ,您可以轻松访问DOM元素并直接与它们进行交互。这对于诸如聚焦输入字段,滚动到特定元素或动画元素之类的任务很有用。
  • 存储不带重新租赁器的可变值:与状态变量不同,更改为 useRef 对象的 current 属性不会触发重新订阅者。这使其成为存储不会影响组件UI的值的绝佳选择。
  • 优化性能 useRef 是优化性能的宝贵工具。您可以使用它来记忆昂贵的计算,以确保仅在必要时重新计算它们。

现在,让我们深入研究 useRef 的内部工作,并了解为什么它不会引起重新租赁。

了解JavaScript中的封闭

要掌握为什么 useRef 不会触发重新租赁,因此必须在Javascript中理解关闭

封闭的基本面

封闭是JavaScript中的一个基本概念,即使在该范围之外执行它,函数也会“记住”其词汇范围。关闭启用函数可以从其包含函数访问变量,即使包含函数完成执行。

考虑这个简单的示例:

function outer() {
  const outerVar = 'I am from outer function';

  function inner() {
    console.log(outerVar); // Accessing outerVar from the closure
  }

  return inner;
}

const innerFunction = outer();
innerFunction(); // Outputs: I am from outer function

在此示例中, inner 可以访问 outerVar 变量,这要归功于封闭。封闭的这种属性对于理解 useRef 如何保留渲染的值。

关闭与 useref 的关系

react的 useRef 利用封闭的封闭,以维持其范围内的 current 。这意味着,即使组件重新渲染, useRef 对象仍然保持不变,并且更改其 current 属性也不会触发重新呈现。

换句话说, useRef 创建了一个封闭式,可捕获其 current 属性,以确保其在渲染之间持续。这就是为什么修改 useRef 's current 属性不会导致您的组件重新渲染。

useref 在普通JavaScript中实施

现在我们了解了关闭,让我们看一下如何在Vanilla JavaScript中实现 useRef 的简化表示。

function useRef(initialValue) {
  const refObject = {
    current: initialValue,
  };

  return refObject;
}

在这个简单的实现中:

  1. 我们定义一个函数 useRef initialValue 作为参数。

  2. 在函数内部,我们创建了一个称为 refObject 的对象,并用 current 属性创建了一个对象,该对象被初始化为 initialValue22 。 /p>

  3. 最后,我们返回 refObject ,可用于访问和更新 current 属性。

这是 useRef 如何在Plain JavaScript中实现的基本表示。但是,在React中, useRef 更强大,更通用,因为它与React的渲染和生命周期系统集成在一起。

不变性和反应渲染

React的渲染机制取决于不变性。当React检测到组件的状态或道具的变化时,它会重新呈现分量。但是, useRef 对象的 current 可以更新属性而不会引起重新渲染的反应。

让我们探索为什么会发生这种情况:

import React, { useRef } from 'react';

function MyComponent() {
  const myRef = useRef(null);

  const handleButtonClick = () => {
    // Modifying the current property doesn't trigger a re-render
    myRef.current.textContent = 'Button Clicked';
  };

  return (
    <div>
      <button onClick={handleButtonClick}>Click Me</button>
      <p ref={myRef}>Initial Text</p>
    </div>
  );
}

在此示例中,当单击按钮时,我们将修改 textContent myRef.current 元素。此更改不会导致组件重新渲染,因为 myRef 对象本身保持不变。

这种行为与React的不变性哲学保持一致。 React通过比较新值和以前的值来标识变化。由于 myRef 对象的身份(即引用本身)在我们更新其 current 属性时不会改变,因此React不认为它是触发的状态或道具更改重新渲染。

反应的身份与和解

要进一步了解为什么 useRef 不会触发重新订阅者,探索React的身份与和解过程至关重要。

解释React的和解过程

React的核心算法(称为对帐)负责确定何时以及如何更新DOM以匹配新的虚拟DOM(vdom)表示。

  1. 虚拟DOM: React保持实际DOM的虚拟表示,称为虚拟DOM(Vdom)。当组件的状态或道具发生变化时,React会生成新的vdom树。

  2. 对帐: React将新的vdom树与前一个树比较,以确定它们之间的差异(或“差异”)。此过程称为对帐。

  3. 最小化更新: React的目标是最大程度地减少对实际DOM的更新数量。它标识了vdom的哪些部分已更改并计算更新DOM以反映这些更改的最有效方法。

  4. 组件身份:确定是否应更新组件,对其身份是否已更改,对组件进行检查。这里的身份是指对组件或元素的引用,该组件或元素由组件树中使用的变量或功能确定。

为什么 useref 对象保留其身份

要注意的关键点是 useRef 对象,包括其 current 属性,保留其在渲染中的身份。当组件重新呈现时,反应可确保 useRef 对象与以前的渲染中保持不变。

在上面的示例中,当单击按钮并且 textContent myRef.current 被修改时, myRef 对象本身保持不变。反应认识到 myRef 对象的身份没有改变,因此不会触发重新渲染。

这种行为与React通过识别真正更改的组件来最大程度地减少对实际DOM的更新的目标一致。

整个渲染的一致性

react竭尽全力确保 useRef 对象的 current 属性在整个渲染中保持一致。让我们探索一些示例以说明这种一致性。

示例1:存储DOM元素参考

import React, { useRef, useEffect } from 'react';

function MyComponent() {
  const myRef = useRef(null);

  useEffect(() => {
    // Access the DOM element using myRef.current
    myRef.current.focus();
  }, []);

  return <input ref={myRef} />;
}

在此示例中, myRef useRef 对象,它持续遍布渲染。它用于创建对 <input> 元素的引用,您可以使用 myRef.current 访问DOM元素。 useEffect 挂钩用于安装组件时,将重点放在输入元件上。

这里的关键要点是, myRef 对象保留其跨渲染的身份,确保 myRef.current 始终指的是同一dom元素。

示例2:回忆值

import React, { useRef, useState } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);
  const doubledCountRef = useRef(null);

  if (!doubledCountRef.current) {
    doubledCountRef.current = count * 2;
  }

  return (
    <div>
      <p>Count: {count}</p>
      <p>Doubled Count (Memoized): {doubledCountRef.current}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

在此示例中,我们使用 useRef 记住 count 的两倍值。 doubledCountRef 对象在渲染范围内持续存在,我们只有在没有记忆之前就计算和记忆了两倍的计数值。

再次, doubledCountRef 对象的身份在整个渲染中保持一致,以确保可访问记忆的价值并最新。

USEREF的常见用例

现在,我们已经涵盖了 useRef 的内部工作,以及为什么它在整个渲染中保留其身份,让我们探索一些通用挂钩的常见用例。

访问和操纵DOM元素

useRef 最常见的用例之一是直接访问和操纵dom元素。当您需要执行诸如专注于输入字段,滚动到特定元素或动画元素之类的操作时,这一点特别有用。

这是一个示例,演示了如何使用 useRef to focus 在输入字段上:

import React, { useRef } from 'react';

function MyComponent() {
  const inputRef = useRef(null);

  const handleFocusButtonClick = () => {
    // Focus on the input element using useRef
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleFocusButtonClick}>Focus Input</button>
    </div>
  );
}

在此示例中, inputRef useRef 对象,它存储了对 <input> 元素的引用。单击“焦点输入”按钮时,执行 inputRef.current.focus() 行,并且输入字段接收到焦点。

存储可变的值,而无需重新租赁

与状态变量不同,更改为 useRef 对象的当前属性不会触发重新汇款。这使得 useRef 是存储不影响组件 ui 但需要在渲染之间持续存在的值的绝佳选择。

这是一个使用 useRef 存储先前值的示例:

import React, { useState, useEffect, useRef } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);
  const previousCountRef = useRef(0);

  useEffect(() => {
    // Update the previous count when count changes
    previousCountRef.current = count;
  }, [count]);

  return (
    <div>
      <p>Current Count: {count}</p>
      <p>Previous Count: {previousCountRef.current}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

在此示例中, previousCountRef useRef 对象,它存储了 count 的先前值。每当 count 更改时,我们都会在 useEffect 挂钩中更新它。存储的值持续存在遍历渲染图,而不会触发重新租赁,从而使我们能够显示先前的计数。

优化性能

useRef 也可以成为优化性能的宝贵工具。您可以使用它来记忆昂贵的计算,以确保仅在必要时重新计算它们。

考虑一个示例,您需要计算一个取决于一组输入的复杂值。您可以使用 useRef 存储和记忆结果:

import React, { useState, useEffect, useRef } from 'react';

function MyComponent() {
  const [inputValue, setInputValue] = useState('');
  const [result, setResult] = useState(null);
  const computationCache = useRef({});

  useEffect(() => {
    if (!computationCache.current[inputValue]) {
      // Perform the expensive calculation and store the result in the cache
      computationCache.current[inputValue] = performExpensiveCalculation(inputValue);
    }

    // Update the result with the cached value
    setResult(computationCache.current[inputValue]);
  }, [inputValue]);

  return (
    <div>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      />
      <p>Result: {result}</p>
    </div>
  );
}

在此示例中,我们将 computationCache 用作 useRef 对象来存储基于 inputValue 的昂贵计算结果。如果特定输入值的结果尚未在缓存中,我们执行计算并将结果存储在缓存中。这通过避免冗余计算来优化性能。

高级 useref 技术

虽然我们涵盖了 useRef 的基础知识,但您可以探索以利用其全部潜力的高级技术和模式。

useref 使用

useRef useEffect 可以组合以处理更复杂的方案。您可以使用 useRef 来管理可变值或参考, useEffect 执行副作用。

这是一个示例,我们将两者结合在一起以观察文档标题中的更改:

import React, { useEffect, useRef } from 'react';

function MyComponent() {
  const documentTitleRef = useRef('');

  useEffect(() => {
    // Update the document title if it has changed
    if (document.title !== documentTitleRef.current) {
      document.title = documentTitleRef.current;
    }
  }, []);

  const handleTitleChange = (e) => {
    documentTitleRef.current = e.target.value;
  };

  return (
    <div>
      <input
        type="text"
        placeholder="Enter new title"
        onChange={handleTitleChange}
      />
    </div>
  );
}

在此示例中, documentTitleRef useRef 用于存储文档标题的对象。我们将其与使用效率相结合,以观察 documentTitleRef.current 值的更改并更新文档 title accordingly 。这种模式使我们能够有效地管理副作用。

最佳实践和建议

当您精通 useRef 时,这里有一些最佳实践和建议:

使用 useRef 出于其预期目的:管理不应触发重新租赁的可变参考和值。

在需要时将 useRef 与其他钩子结合在一起。例如,将其与 useEffect 结合使用以管理副作用或 useContext 访问上下文值。

注意初始化。确保适当初始化useref对象,尤其是在使用DOM元素时。

结论

在本综合指南中,我们探索了React的 useRef 深度。我们已经了解了 useRef 如何利用封闭,保留其跨渲染的身份,并提供了一种多功能工具,用于访问和操纵dom元素,存储可变值并优化性能。我们还涵盖了高级技术和最佳实践,以帮助您成为 useRef 的大师。

当您继续构建React应用程序时,请记住, useRef 不仅是访问DOM的工具;它是管理状态和优化性能的强大工具。无论您是React新手还是经验丰富的开发人员,掌握 useRef 无疑都会提高您的反应技巧,并使您成为更熟练的开发人员。

所以继续前进,利用 useRef 的力量,并自信地建立更有效,更有绩效的应用​​程序!