何时使用 useImperativeHandle、useLayoutEffect 和 useDebugValue

2024-03-16

我不明白为什么以下useImperativeHandle, useLayoutEffect, and useDebugValue需要钩子,您能否给出可以使用它们的示例,但请不要提供文档中的示例。


请允许我在这个答案的前言中指出,所有这些钩子都很少使用。 99% 的情况下,您不需要这些。它们只是为了涵盖一些罕见的极端情况。


useImperativeHandle

通常当你使用useRef您将获得组件的实例值ref附于.这允许您直接与 DOM 元素交互。

useImperativeHandle非常相似,但它可以让你做两件事:

  1. 它使您可以控制返回的值。您不返回实例元素,而是明确声明返回值是什么(请参见下面的代码片段)。
  2. 它允许您替换本机函数(例如blur, focus等)具有您自己的功能,从而允许对正常行为产生副作用,或者完全不同的行为。不过,您可以随意调用该函数。

您想要执行上述任一操作的原因可能有很多;您可能不想向父级公开本机属性,或者您可能想更改本机函数的行为。原因可能有很多。然而,useImperativeHandle很少使用。

useImperativeHandle自定义使用时暴露给父组件的实例值ref

Example

在此示例中,我们将从中获得的值ref将仅包含该功能blur我们在我们的useImperativeHandle。它不会包含任何其他属性(我正在记录该值来证明这一点)。该函数本身也经过“定制”,其行为与您通常期望的不同。这里,设置了document.title并模糊输入blur被调用。

const MyInput = React.forwardRef((props, ref) => {
  const [val, setVal] = React.useState('');
  const inputRef = React.useRef();

  React.useImperativeHandle(ref, () => ({
    blur: () => {
      document.title = val;
      inputRef.current.blur();
    }
  }));

  return (
    <input
      ref={inputRef}
      val={val}
      onChange={e => setVal(e.target.value)}
      {...props}
    />
  );
});

const App = () => {
  const ref = React.useRef(null);
  const onBlur = () => {
    console.log(ref.current); // Only contains one property!
    ref.current.blur();
  };

  return <MyInput ref={ref} onBlur={onBlur} />;
};

ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>

useLayoutEffect

虽然在某种程度上类似于useEffect(),它的不同之处在于它将在 React 向 DOM 提交更新后运行。在极少数情况下使用,当您需要在更新后计算元素之间的距离或进行其他更新后计算/副作用时。

签名与useEffect,但它会在所有 DOM 突变后同步触发。使用它从 DOM 读取布局并同步重新渲染。内部计划更新useLayoutEffect将被同步刷新,在浏览器有机会绘制之前.

Example

假设您有一个绝对定位的元素,其高度可能会有所不同,并且您想要定位另一个元素div在它下面。你可以使用getBoundingClientRect()计算父级的高度和顶部属性,然后将它们应用到子级的顶部属性。

在这里你想使用useLayoutEffect而不是useEffect。请参阅下面的示例了解原因:

With useEffect:(注意跳跃行为)

const Message = ({boxRef, children}) => {
  const msgRef = React.useRef(null);
  React.useEffect(() => {
    const rect = boxRef.current.getBoundingClientRect();
    msgRef.current.style.top = `${rect.height + rect.top}px`;
  }, []);

  return <span ref={msgRef} className="msg">{children}</span>;
};

const App = () => {
  const [show, setShow] = React.useState(false);
  const boxRef = React.useRef(null);

  return (
    <div>
      <div ref={boxRef} className="box" onClick={() => setShow(prev => !prev)}>Click me</div>
      {show && <Message boxRef={boxRef}>Foo bar baz</Message>}
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("app"));
.box {
  position: absolute;
  width: 100px;
  height: 100px;
  background: green;
  color: white;
}

.msg {
  position: relative;
  border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>

With useLayoutEffect:

const Message = ({boxRef, children}) => {
  const msgRef = React.useRef(null);
  React.useLayoutEffect(() => {
    const rect = boxRef.current.getBoundingClientRect();
    msgRef.current.style.top = `${rect.height + rect.top}px`;
  }, []);

  return <span ref={msgRef} className="msg">{children}</span>;
};

const App = () => {
  const [show, setShow] = React.useState(false);
  const boxRef = React.useRef(null);

  return (
    <div>
      <div ref={boxRef} className="box" onClick={() => setShow(prev => !prev)}>Click me</div>
      {show && <Message boxRef={boxRef}>Foo bar baz</Message>}
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("app"));
.box {
  position: absolute;
  width: 100px;
  height: 100px;
  background: green;
  color: white;
}

.msg {
  position: relative;
  border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>

useDebugValue

有时您可能想要调试某些值或属性,但这样做可能需要昂贵的操作,这可能会影响性能。

useDebugValue仅当 React DevTools 打开并检查相关钩子时才调用,以防止对性能产生任何影响。

useDebugValue可用于在 React DevTools 中显示自定义挂钩的标签。

但我个人从未使用过这个钩子。也许评论中的人可以通过一个很好的例子来提供一些见解。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

何时使用 useImperativeHandle、useLayoutEffect 和 useDebugValue 的相关文章

随机推荐

  • 如何使用 Jersey(测试框架)将数据源依赖项注入到 RESTful Web 服务中?

    我正在使用 Jersey 构建一个 RESTful Web 服务 该服务依赖 MongoDB 来实现持久性 Web 服务本身连接到默认数据库 但对于单元测试 我想使用单独的测试数据库 我将在 setUp 中填充此测试数据库 运行测试 然后在
  • 每张信用卡的 paypal payer_id 是唯一的吗? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我想知道使用同一张信用卡的两次单独付款的 paypal payer id 是否相同 这显然对于检测可能的欺诈交易很有用 请注意 对于 paypal 帐
  • Vim 脚本突出显示大括号等标签的末尾

    我需要一个 Vim 脚本来在光标位于标签开头时突出显示标签的结尾 例如 在 html 标签中 当光标位于标签开头时 它应该突出显示标签结尾 接口应该是通用的 以便可以添加更多标签 如果您将 html vim 文件替换为this http w
  • 空值可以传递给包裹吗?

    是否可以写null to Parcel当分割一个对象时 得到null再次解压时又回来了 假设我们有以下代码 public class Test implements Parcelable private String string null
  • 在调用 Main() 之前 Windows 会做什么?

    Windows 必须做一些事情来解析 PE 标头 将可执行文件加载到内存中 并将命令行参数传递给main Using OllyDbg I have set the debugger to break on main so I could v
  • Twitter API请求限制问题

    我编写了一个小型 java 程序来从 Twitter 下载所有朋友和关注者的个人资料图片 但我收到错误 因为每个 IP 地址每小时只允许 150 个请求 确切的错误是 twitter4j TwitterException 400 The r
  • SELECT 语句中的子查询 (MySQL)

    我正在创建一个 SQL 语句 它将返回一个产品列表以及我在每个商店中可以找到的每种产品的数量 我的表的结构 带有一些示例数据 如下 productID size color stock storeID 1 S RED01 1 BCN 1 S
  • 如何根据单元格值有条件地设置 ReactJs 材料表单元格的样式?

    我在材料表中有一个列 其中包含成功 失败等值 根据这些值 我需要在单元格上应用颜色 如何使用材质表来实现 这个答案是专门针对反应材料表 https material table com 在列部分中 我们需要具有如下所述的内容 因此当在表中呈
  • 调用中参数“coder”缺少参数

    我将自定义 UIButton 编码为 class AccountOpeningButton UIButton required init coder aDecoder NSCoder super init coder aDecoder 我能
  • 无法在当前范围或上下文中解决。确保所有引用的变量都在范围内

    我收到此错误 无法在当前范围或上下文中解析 TblProduct 请确保所有引用的变量都在范围内 加载所需的架构 并且正确引用命名空间 在下面的代码中 我不确定为什么它不能正常工作 我希望有人能够提供帮助 谢谢 private void A
  • 短数组的最佳排序函数

    我正在研究一种处理图片的算法 基本上我将实现一个扩散 每个像素将获得周围 8 个像素的中值 它自己的值 我要做的就是使用该值创建一个包含 9 个整数的数组 对数组进行排序并获取 array 4 处的中值 我仍然不知道该使用什么来解决这个问题
  • 根据需要验证正文中的不可空属性 - AspNetCore 3.1

    我正在尝试验证是否在请求中完全忽略了属性 字段 即 ModelState 无效并且 BadRequest 被发送回客户端 但是我正在努力处理请求主体中的不可空类型 适用于可空类型 Required public string Nullabl
  • 使用 SPARK 证明选择排序算法

    我试图证明我在 Ada 中的选择排序实现是正确的 我尝试了一些循环不变量 但使用 gnatprove 只能证明内部循环的不变量 package body Selection with SPARK Mode is procedure Sort
  • 在 Swift 中设置活动标签栏项目的背景颜色

    如果可能的话 我希望在不使用图像的情况下完成此任务 有没有一种方法可以以编程方式创建图像中显示的效果 而不必将每个选项卡渲染为图像 我在 SO 上审阅的每个问题都将选项卡保存为 JPG 这比我认为应该做的工作要多 Any ideas 我采用
  • 使用gdb将地址转换为行

    我有一个由剥离的应用程序生成的堆栈跟踪 如下所示 Check failure stack trace 0x7f0e442d392d unknown 0x7f0e442d7b1f unknown 0x7f0e442d7067 unknown
  • SwiftUI 中的事件触发的动画

    SwiftUI 动画通常由状态驱动 这很好 但有时您确实想触发临时 通常是可逆的 动画来响应某些事件 例如 我想在点击按钮时暂时增加按钮的大小 释放按钮时大小的增加和减小都应作为单个动画发生 但我无法弄清楚这一点 我认为它可以与转换结合在一
  • 将新文件添加到 subversion 而不更新整个文件夹

    我想要做的是能够将一个文件添加到本地计算机中 Subversion 源代码控制下的文件夹中 然后提交它 到这里没问题 在服务器上 我想使用 Subversion 获取新文件 但不必对整个文件夹进行 更新 这可能会更新一堆不相关的文件 你看
  • Java:如何从静态上下文中获取当前类的类对象?

    我有一个日志记录函数 它将调用对象作为参数 然后我对其调用 getClass getSimpleName 以便我可以轻松获取类名称以添加到我的日志条目中以方便参考 问题是 当我从静态方法调用日志函数时 我无法传入 this 我的日志函数看起
  • 在代码中与在脚本中生成集成测试数据[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 这个问题很可能是基于意见的 但我确信 有确凿论据支持的观点将为明智的决策铺平道路 我确实喜欢使用 Autofixture 生成数据库状态 我真诚
  • 何时使用 useImperativeHandle、useLayoutEffect 和 useDebugValue

    我不明白为什么以下useImperativeHandle useLayoutEffect and useDebugValue需要钩子 您能否给出可以使用它们的示例 但请不要提供文档中的示例 请允许我在这个答案的前言中指出 所有这些钩子都很少