React 18 使用 ref 操作 DOM

这篇具有很好参考价值的文章主要介绍了React 18 使用 ref 操作 DOM。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

参考文章

使用 ref 操作 DOM

由于 React 会自动处理更新 DOM 以匹配渲染输出,因此在组件中通常不需要操作 DOM。但是,有时可能需要访问由 React 管理的 DOM 元素 —— 例如,让一个节点获得焦点、滚动它或测量它的尺寸和位置。在 React 中没有内置的方法来做这些事情,所以需要一个指向 DOM 节点的 ref 来实现。

获取指向节点的 ref

要访问由 React 管理的 DOM 节点,首先,引入 useRef Hook:

import { useRef } from 'react';

然后,在组件中使用它声明一个 ref:

const myRef = useRef(null);

最后,将ref作为“ref”属性传递给要获取其DOM节点的JSX标记:

<div ref={myRef}>

useRef Hook 返回一个对象,该对象有一个名为 current 的属性。最初,myRef.currentnull。当 React 为这个 <div> 创建一个 DOM 节点时,React 会把对该节点的引用放入 myRef.current。然后,可以从 事件处理器 访问此 DOM 节点,并使用在其上定义的内置浏览器 API。

// 可以使用任意浏览器 API,例如:

myRef.current.scrollIntoView();

示例: 使文本输入框获得焦点

在本例中,单击按钮将使输入框获得焦点:

import { useRef } from 'react';

export default function Form() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>
        聚焦输入框
      </button>
    </>
  );
}

要实现这一点:

  1. 使用 useRef Hook 声明 inputRef
  2. <input ref={inputRef}> 这样传递它。这告诉 React 将这个 <input> 的 DOM 节点放入 inputRef.current
  3. handleClick 函数中,从 inputRef.current 读取 input DOM 节点并使用 inputRef.current.focus() 调用它的 focus()
  4. onClickhandleClick 事件处理器传递给 <button>

虽然 DOM 操作是 ref 最常见的用例,但 useRef Hook 可用于存储 React 之外的其他内容,例如计时器 ID 。与 state 类似,ref 能在渲染之间保留。甚至可以将 ref 视为设置它们时不会触发重新渲染的 state 变量!

示例: 滚动至一个元素

一个组件中可以有多个 ref。在这个例子中,有一个由三张图片和三个按钮组成的轮播,点击按钮会调用浏览器的 scrollIntoView() 方法,在相应的 DOM 节点上将它们居中显示在视口中:

import { useRef } from 'react';

export default function CatFriends() {
  const firstCatRef = useRef(null);
  const secondCatRef = useRef(null);
  const thirdCatRef = useRef(null);

  function handleScrollToFirstCat() {
    firstCatRef.current.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest',
      inline: 'center'
    });
  }

  function handleScrollToSecondCat() {
    secondCatRef.current.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest',
      inline: 'center'
    });
  }

  function handleScrollToThirdCat() {
    thirdCatRef.current.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest',
      inline: 'center'
    });
  }

  return (
    <>
      <nav>
        <button onClick={handleScrollToFirstCat}>
          Tom
        </button>
        <button onClick={handleScrollToSecondCat}>
          Maru
        </button>
        <button onClick={handleScrollToThirdCat}>
          Jellylorum
        </button>
      </nav>
      <div>
        <ul>
          <li>
            <img
              src="https://placekitten.com/g/200/200"
              alt="Tom"
              ref={firstCatRef}
            />
          </li>
          <li>
            <img
              src="https://placekitten.com/g/300/200"
              alt="Maru"
              ref={secondCatRef}
            />
          </li>
          <li>
            <img
              src="https://placekitten.com/g/250/200"
              alt="Jellylorum"
              ref={thirdCatRef}
            />
          </li>
        </ul>
      </div>
    </>
  );
}

访问另一个组件的 DOM 节点

当将 ref 放在像 <input /> 这样的浏览器元素的内置标签上时,React 会将该 ref 的 current 属性设置为相应的 DOM 节点(例如浏览器中实际的 <input /> )。

但是,如果尝试将 ref 放在 自定义 组件上,例如 <MyInput />,默认情况下会得到 null。这个示例演示了这种情况。请注意单击按钮 并不会 聚焦输入框:

import { useRef } from 'react';

function MyInput(props) {
  return <input {...props} />;
}

export default function MyForm() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <MyInput ref={inputRef} />
      <button onClick={handleClick}>
        聚焦输入框
      </button>
    </>
  );
}

为了帮助您注意到这个问题,React 还会向控制台打印一条错误消息:

Console

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

警告:函数组件不能给出refs。尝试访问这个ref将失败。你的意思是使用React.forwardRef() 吗?

发生这种情况是因为默认情况下,React 不允许组件访问其他组件的 DOM 节点。甚至自己的子组件也不行!这是故意的。Refs 是一个应急方案,应该谨慎使用。手动操作 另一个 组件的 DOM 节点会使你的代码更加脆弱。

相反,想要 暴露其 DOM 节点的组件必须选择该行为。一个组件可以指定将它的 ref “转发”给一个子组件。下面是 MyInput 如何使用 forwardRef API:

const MyInput = forwardRef((props, ref) => {
  return <input {...props} ref={ref} />;
});

它是这样工作的:

  1. <MyInput ref={inputRef} /> 告诉 React 将对应的 DOM 节点放入 inputRef.current 中。但是,这取决于 MyInput 组件是否允许这种行为, 默认情况下是不允许的。
  2. MyInput 组件是使用 forwardRef 声明的。 这让从上面接收的 inputRef 作为第二个参数 ref 传入组件,第一个参数是 props
  3. MyInput 组件将自己接收到的 ref 传递给它内部的 <input>

现在,单击按钮聚焦输入框起作用了:

import { forwardRef, useRef } from 'react';

const MyInput = forwardRef((props, ref) => {
  return <input {...props} ref={ref} />;
});

export default function Form() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <MyInput ref={inputRef} />
      <button onClick={handleClick}>
        聚焦输入框
      </button>
    </>
  );
}

在设计系统中,将低级组件(如按钮、输入框等)的 ref 转发到它们的 DOM 节点是一种常见模式。另一方面,像表单、列表或页面段落这样的高级组件通常不会暴露它们的 DOM 节点,以避免对 DOM 结构的意外依赖。

React 何时添加 refs

在 React 中,每次更新都分为 两个阶段:

  • 渲染 阶段, React 调用组件来确定屏幕上应该显示什么。
  • 提交 阶段, React 把变更应用于 DOM。

通常, 不希望 在渲染期间访问 refs。这也适用于保存 DOM 节点的 refs。在第一次渲染期间,DOM 节点尚未创建,因此 ref.current 将为 null。在渲染更新的过程中,DOM 节点还没有更新。所以读取它们还为时过早。

React 在提交阶段设置 ref.current。在更新 DOM 之前,React 将受影响的 ref.current 值设置为 null。更新 DOM 后,React 立即将它们设置到相应的 DOM 节点。

通常,将从事件处理器访问 refs。 如果想使用 ref 执行某些操作,但没有特定的事件可以执行此操作,可能需要一个 effect。

使用 refs 操作 DOM 的最佳实践

Refs 是一个应急方案。应该只在必须“跳出 React”时使用它们。这方面的常见示例包括管理焦点、滚动位置或调用 React 未暴露的浏览器 API。

如果坚持聚焦和滚动等非破坏性操作,应该不会遇到任何问题。但是,如果尝试手动修改 DOM,则可能会与 React 所做的更改发生冲突。

为了说明这个问题,这个例子包括一条欢迎消息和两个按钮。第一个按钮使用 条件渲染 和 state 切换它的显示和隐藏,就像通常在 React 中所做的那样。第二个按钮使用 remove() DOM API 将其从 React 控制之外的 DOM 中强行移除.

尝试按几次“通过 setState 切换”。该消息会消失并再次出现。然后按 “从 DOM 中删除”。这将强行删除它。最后,按 “通过 setState 切换”:

import { useState, useRef } from 'react';

export default function Counter() {
  const [show, setShow] = useState(true);
  const ref = useRef(null);

  return (
    <div>
      <button
        onClick={() => {
          setShow(!show);
        }}>
        通过 setState 切换
      </button>
      <button
        onClick={() => {
          ref.current.remove();
        }}>DOM 中删除
      </button>
      {show && <p ref={ref}>Hello world</p>}
    </div>
  );
}

在手动删除 DOM 元素后,尝试使用 setState 再次显示它会导致崩溃。这是因为更改了 DOM,而 React 不知道如何继续正确管理它。

避免更改由 React 管理的 DOM 节点。 对 React 管理的元素进行修改、添加子元素、从中删除子元素会导致不一致的视觉结果,或与上述类似的崩溃。

但是,这并不意味着完全不能这样做。它需要谨慎。 可以安全地修改 React 没有理由 更新的部分 DOM。 例如,如果某些 <div> 在 JSX 中始终为空,React 将没有理由去变动其子列表。 因此,在那里手动增删元素是安全的。文章来源地址https://www.toymoban.com/news/detail-495156.html

摘要

  • Refs 是一个通用概念,但大多数情况下会使用它们来保存 DOM 元素。
  • 通过传递 <div ref={myRef}> 指示 React 将 DOM 节点放入 myRef.current
  • 通常,会将 refs 用于非破坏性操作,例如聚焦、滚动或测量 DOM 元素。
  • 默认情况下,组件不暴露其 DOM 节点。 可以通过使用 forwardRef 并将第二个 ref 参数传递给特定节点来暴露 DOM 节点。
  • 避免更改由 React 管理的 DOM 节点。
  • 如果确实修改了 React 管理的 DOM 节点,请修改 React 没有理由更新的部分。

到了这里,关于React 18 使用 ref 操作 DOM的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用React18+Ts创建项目

    首先,使用create-react-app工具创建一个新的React项目: 使用脚手架创建项目后,自带react-dom等依赖项,但react中的所用的路由方法是react-router-dom中。 默认情况下,create-react-app模板会自动生成一些文件和文件夹,这些文件和文件夹包括: node_modules:存储所有的项目依赖项。 p

    2024年02月09日
    浏览(32)
  • React 18中新钩子 useDeferredValue 使用

    React是一个流行的用于构建用户界面的JavaScript库,它不断发展以为开发人员提供优化性能的工具。 React 18中引入的此类工具之一是 useDeferredValue 钩子,它旨在通过优先渲染更新来提高应用程序的性能。 useDeferredValue 钩子是React性能优化工具集中相对较新的补充。 它在处理异步数

    2024年01月19日
    浏览(30)
  • React 18 使用 Context 深层传递参数

    参考文章 通常来说,会通过 props 将信息从父组件传递到子组件。但是,如果必须通过许多中间组件向下传递 props,或是在应用中的许多组件需要相同的信息,传递 props 会变的十分冗长和不便。 Context 允许父组件向其下层无论多深的任何组件提供信息,而无需通过 props 显式传

    2024年02月09日
    浏览(25)
  • 使用React 18和WebSocket构建实时通信功能

    WebSocket 是一种在 Web 应用中实现双向通信的协议。它允许服务器主动向客户端推送数据,而不需要客户端发起请求。在现代的实时应用中, WebSocket 经常用于实时数据传输、聊天功能、实时通知和多人协作等场景。在本篇博客中,我们将探索如何在 React 18 应用中使用 WebSocke

    2024年02月05日
    浏览(32)
  • React18TS项目:配置react-css-modules,使用styleName

    他的好处不说了 网上一堆文章一个能打的都没有, 添加开发依赖 Babel Plugin \\\"React CSS Modules\\\" | Dr. Pogodin Studio 看@dr.pogodin/babel-plugin-react-css-modules官方文档 不使用babel-plugin-react-css-modules 手搭webpack配置需要处理 1.能启用css modules 对于裸 Webpack,请参见webpack css-loader的 modules 的选项

    2024年02月12日
    浏览(34)
  • React18.x + i18next + antd 国际化正确使用姿势及避坑指南

    如果你使用这个教程还不能够解决你的问题的话,直接私信我,免费一对一给你解决。 具体的创建方法大家参考vite官方文档,大概的操作如下,如果需要更详细的,大家去自行搜索即可 因为我这里使用的是ts版本,所以,你自己看着办吧。 其中 i18next-browser-languagedetector i1

    2024年02月05日
    浏览(45)
  • React 中 Ref 引用

    不要因为别人的评价而改变自己的想法,因为你的生活是你自己的。 给标签设置 ref,ref=\\\"username\\\", 通过 this.refs.username 可以获取引用的标签,ref 可以获取到应用的真实 Dom 节点。但是 this.refs 已被废弃。 同上标签设置 ref 给组件设置 ref 也保持一致,ref=\\\"username\\\", 通过 this.r

    2024年02月05日
    浏览(28)
  • React三属性之:refs

    作用 refs是为了获取节点,使用场景主要在需要操作dom的时候,比如echarts,就需要真实的dom节点 使用 效果如下

    2024年02月09日
    浏览(23)
  • React:高阶组件|ref转发

            参考文档:高阶组件 – React (reactjs.org)         高阶组件(Higher-Order Components,简称  HOC )是React中用于复用组件逻辑的一种高级技巧。具体而言: 高阶组件是参数为组件,返回值为新组件的函数 。         组件是将 props 转换为 UI,而高阶组件是将组件转换

    2024年02月21日
    浏览(31)
  • 浅谈React中的ref和useRef

    目录 什么是useRef? 使用 ref 访问 DOM 元素 Ref和useRef之间的区别 Ref和useRef的使用案例 善用工具 结论 在各种  JavaScript  库和框架中, React  因其开发人员友好性和支持性而得到认可。 大多数开发人员发现  React  非常舒适且可扩展,因为它提供了钩子。钩子是  React  附带的

    2024年02月14日
    浏览(23)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包