带你彻底搞懂useLayoutEffect的使用场景

开篇第一句: useLayoutEffect 可能会影响性能。尽可能使用 useEffect。

useLayoutEffect 是 useEffect 的一个版本,在浏览器重新绘制屏幕之前触发。

使用方法

useLayoutEffect(setup, dependencies?)

调用 useLayoutEffect 在浏览器重新绘制屏幕之前进行布局测量:

import { useState, useRef, useLayoutEffect } from 'react';function Tooltip() {const ref = useRef(null);const [tooltipHeight, setTooltipHeight] = useState(0);useLayoutEffect(() => {const { height } = ref.current.getBoundingClientRect();setTooltipHeight(height);}, []);// ...

参数

  • setup:处理副作用的函数。setup 函数选择性返回一个清理(cleanup)函数。在将组件首次添加到 DOM 之前,React 将运行 setup 函数。在每次因为依赖项变更而重新渲染后,React 将首先使用旧值运行 cleanup 函数(如果你提供了该函数),然后使用新值运行 setup 函数。在组件从 DOM 中移除之前,React 将最后一次运行 cleanup 函数。

  • 可选 dependencies:setup 代码中引用的所有响应式值的列表。响应式值包括 props、state 以及所有直接在组件内部声明的变量和函数。如果你的代码检查工具 配置了 React,那么它将验证每个响应式值都被正确地指定为一个依赖项。依赖项列表必须具有固定数量的项,并且必须像 [dep1, dep2, dep3] 这样内联编写。React 将使用 Object.is 来比较每个依赖项和它先前的值。如果省略此参数,则在每次重新渲染组件之后,将重新运行副作用函数。

返回值

useLayoutEffect 返回 undefined。

注意事项

  • useLayoutEffect 是一个 Hook,因此只能在 组件的顶层 或自己的 Hook 中调用它。不能在循环或者条件内部调用它。如果你需要的话,抽离出一个组件并将副作用处理移动到那里。

  • 当 StrictMode 启用时,React 将在真正的 setup 函数首次运行前,运行一个额外的开发专有的 setup + cleanup 周期。这是一个压力测试,确保 cleanup 逻辑“映照”到 setup 逻辑,并停止或撤消 setup 函数正在做的任何事情。如果这导致一个问题,请实现清理函数。

  • 如果你的一些依赖项是组件内部定义的对象或函数,则存在这样的风险,即它们将 导致 Effect 重新运行的次数多于所需的次数。要解决这个问题,请删除不必要的 对象 和 函数 依赖项。你还可以 抽离状态更新 和 非响应式逻辑 到 Effect 之外。

  • Effect 只在客户端上运行,在服务端渲染中不会运行。

  • useLayoutEffect 内部的代码和所有计划的状态更新阻塞了浏览器重新绘制屏幕。如果过度使用,这会使你的应用程序变慢。如果可能的话,尽量选择 useEffect。


案例

在浏览器重新绘制屏幕前计算布局

大多数组件不需要依靠它们在屏幕上的位置和大小来决定渲染什么。他们只返回一些 JSX,然后浏览器计算他们的 布局(位置和大小)并重新绘制屏幕。

有时候,这还不够。想象一下悬停时出现在某个元素旁边的 tooltip。如果有足够的空间,tooltip 应该出现在元素的上方,但是如果不合适,它应该出现在下面。为了让 tooltip 渲染在最终正确的位置,你需要知道它的高度(即它是否适合放在顶部)。

要做到这一点,你需要分两步渲染:

  1. 将 tooltip 渲染到任何地方(即使位置不对)。
  2. 测量它的高度并决定放置 tooltip 的位置。
  3. 把 tooltip 渲染放在正确的位置。

所有这些都需要在浏览器重新绘制屏幕之前完成。你不希望用户看到 tooltip 在移动。调用 useLayoutEffect 在浏览器重新绘制屏幕之前执行布局测量:

function Tooltip() {const ref = useRef(null);const [tooltipHeight, setTooltipHeight] = useState(0); // 你还不知道真正的高度useLayoutEffect(() => {const { height } = ref.current.getBoundingClientRect();setTooltipHeight(height); // 现在重新渲染,你知道了真实的高度}, []);// ... 在下方的渲染逻辑中使用 tooltipHeight ...
}

下面是这如何一步步工作的:

  1. Tooltip 使用初始值 tooltipHeight = 0 进行渲染(因此 tooltip 可能被错误地放置)。
  2. React 将它放在 DOM 中,然后运行 useLayoutEffect 中的代码。
  3. useLayoutEffect 测量 了 tooltip 内容的高度,并立即触发重新渲染。
  4. 使用实际的 tooltipHeight 再次渲染 Tooltip(这样 tooltip 的位置就正确了)。
  5. React 在 DOM 中对它进行更新,浏览器最终显示出 tooltip。

将鼠标悬停在下面的按钮上,看看 tooltip 是如何根据它是否合适来调整它的位置:


export default function App() {return (<div><ButtonWithTooltiptooltipContent={<div>tooltip在按钮下方<br />我在哪里</div>}>鼠标、鼠标,在脚下</ButtonWithTooltip><div style={{ height: 50 }} /><ButtonWithTooltiptooltipContent={<div>tooltip在按钮头顶</div>}>鼠标、鼠标,在头顶</ButtonWithTooltip><div style={{ height: 50 }} /><ButtonWithTooltiptooltipContent={<div>tooltip在按钮头顶</div>}>鼠标、鼠标,在头顶</ButtonWithTooltip></div>);
}function ButtonWithTooltip({ tooltipContent, ...rest }) {const [targetRect, setTargetRect] = useState(null);const buttonRef = useRef(null);return (<><button{...rest}ref={buttonRef}onPointerEnter={() => {const rect = buttonRef.current.getBoundingClientRect();setTargetRect({left: rect.left,top: rect.top,right: rect.right,bottom: rect.bottom,});}}onPointerLeave={() => {setTargetRect(null);}}/>{targetRect !== null && (<Tooltip targetRect={targetRect}>{tooltipContent}</Tooltip>)}</>);
}function Tooltip({ children, targetRect }) {const ref = useRef(null);const [tooltipHeight, setTooltipHeight] = useState(0);useLayoutEffect(() => {const { height } = ref.current.getBoundingClientRect();setTooltipHeight(height);}, []);let tooltipX = 0;let tooltipY = 0;if (targetRect !== null) {tooltipX = targetRect.left;tooltipY = targetRect.top - tooltipHeight;if (tooltipY < 0) {// 它不适合上方,因此把它放在下面。tooltipY = targetRect.bottom;}}return createPortal(<TooltipContainer x={tooltipX} y={tooltipY} contentRef={ref}>{children}</TooltipContainer>,document.body);
}function TooltipContainer({ children, x, y, contentRef }) {return (<divstyle={{position: 'absolute',pointerEvents: 'none',left: 0,top: 0,transform: `translate3d(${x}px, ${y}px, 0)`}}><div ref={contentRef} className="tooltip">{children}</div></div>);
}

效果如下

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/397882.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

lvs详解及实例配置

目录 1.什么是负载均衡 1.1为什么用负载均衡 1.2.负载均衡类型 1.2.1.四层负载均衡 1.2.2.七层负载均衡 1.3 四层和七层的区别 2.LVS介绍 2.1LVS 的优势与不足 2.2LVS 核心组件和专业术语 3.ipvsadm命令 4.LVS集群中的增删改 4.1.管理集群服务中的增删改 4.2.管理集…

用户态tcp协议栈四次挥手-服务端发送fin时,客户端不返回ac

问题&#xff1a; 四次挥手时&#xff0c;服务端发送fin后&#xff0c;客户端不发送ack&#xff0c;反而过了2min后发了个rst报文 62505是客户端&#xff0c;8889是服务端 解决&#xff1a; 服务端返回fin报文时带上ack标记

数据结构(邓俊辉)学习笔记】优先级队列 03——完全二叉堆:结构

文章目录 1.完全二叉树2.结构性3.形神具备4.堆序性 1.完全二叉树 在上一节我们看到&#xff0c;就优先级队列的实现方式而言&#xff0c;采用基本的向量结构并不足够&#xff0c;而采用更高级的树形结构&#xff0c;虽然完全可以高效率地实现优先级队列&#xff0c;但却有杀鸡…

Codeforces Round 961 【C. Squaring】

C. Squaring 题目大意&#xff1a; 给你一个长度为n的数组&#xff0c;求最少次操作&#xff0c;使得数组&#xff08;非严格&#xff09;递增。一次操作&#xff1a;Ai 变为 Ai^2。 不可能实现输出-1。 关键思路&#xff1a; 分子分母同时取对数&#xff0c;比值不变。 …

流量日志分析

流量分析 [陇剑杯 2021]jwt&#xff08;问1&#xff09; 要求是通过流量分析判断网站使用的认证方式 在筛选http之后&#xff0c;发现有get请求&#xff0c;要认证方式就需要看请求流量包中的token 然后查看token tokeneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTAwOD…

动手学深度学习7.3 网络中的网络(NiN)-笔记练习(PyTorch)

以下内容为结合李沐老师的课程和教材补充的学习笔记&#xff0c;以及对课后练习的一些思考&#xff0c;自留回顾&#xff0c;也供同学之人交流参考。 本节课程地址&#xff1a;26 网络中的网络 NiN【动手学深度学习v2】_哔哩哔哩_bilibili 本节教材地址&#xff1a;7.3. 网络…

三层架构与解耦——IoCDI机制【后端 7】

三层架构与解耦——IoC&DI机制 在软件开发领域&#xff0c;三层架构&#xff08;Controller、Service、Dao&#xff09;是一种广泛采用的架构模式&#xff0c;它通过将应用程序分为三个主要层次来组织代码&#xff0c;旨在提高代码的可维护性、复用性和可扩展性。而解耦&am…

怎么用dos编译python

无论windos还是Linux只要安装了python&#xff0c;配置好了环境变量&#xff0c;则在命令行输入python这个命令的时候就会进入交互模式。在这个模式下可以进行一些简单的python代码编写。退出可以使用exit()方法。 Python程序是可以执行的&#xff0c;例如下面代码&#xff0c…

文心一言 VS 讯飞星火 VS chatgpt (323)-- 算法导论22.4 4题

四、证明或反证下述论断&#xff1a;如果有向图G包含环路&#xff0c;则在算法TOPOLOGICAL-SORT(G)所生成的结点序列里&#xff0c;图G中与所生成序列不一致的“坏”边的条数最少。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 首先&#xff0c;我们需要明确几…

春秋云镜CVE-2023-38836

打开靶场环境 点击发现一个登陆框&#xff0c;弱口令试一下 发现账号密码为admin,password 随便点击点击 Media发现这里可以上传文件上传木马试试 <?php eval($_POST["wjq"]); ?> 发现不能上传php文件 php内容 修改他的格式 抓包绕过一下 302就可以其实已经…

同态加密和SEAL库的介绍(六)BGV 方案

前面介绍 BFV 和 CKKS 加密方案&#xff0c;这两者更为常用。并且也解释了 Batch Encoder 和 级别的概念&#xff0c;这对接下来演示 BGV 会很有帮助。 一、BGV简介 BGV (Brakerski-Gentry-Vaikuntanathan) 方案 是一种基于环学习同态加密&#xff08;RLWE&#xff09;问题的加…

华为OD笔试

机试总分400。三道题目。100&#xff0b;100&#xff0b;200 华为od考试时间为150分钟&#xff0c;共有三道编程题&#xff0c;分数分别为100、100和200。如果你是目标院校&#xff08;查看目标院校请戳&#xff09;的话&#xff0c;及格线是160分&#xff0c;非目标院校则不确…

论文阅读 - Scaling Up k-Clique Densest Subgraph Detection | SIGMOD 2023

1. 论文背景 密集子图发现&#xff08;Densest Subgraph Discovery&#xff09;是图挖掘领域的一个基础研究方向&#xff0c;并且近年来在多个应用领域得到了广泛研究。特别是在生物学、金融学和社交网络分析等领域&#xff0c;密集子图的发现对理解复杂网络结构和行为具有重要…

利用QT和FFmpeg实现一个简单的视频播放器

在当今的多媒体世界中&#xff0c;视频播放已成为不可或缺的一部分。从简单的媒体播放器到复杂的视频编辑软件&#xff0c;视频解码和显示技术无处不在。本示例使用Qt和FFmpeg构建一个简单的视频播放器。利用ffmpeg解码视频&#xff0c;通过QWidget渲染解码后的图像&#xff0c…

Python爬虫——爬取bilibili中的视频

爬取bilibili中的视频 本次爬取&#xff0c;还是运用的是requests方法 首先进入bilibili官网中&#xff0c;选取你想要爬取的视频&#xff0c;进入视频播放页面&#xff0c;按F12&#xff0c;将网络中的名称栏向上拉找到第一个并点击&#xff0c;可以在标头中&#xff0c;找到…

Ps:通过 RGB 值计算 HSB 值

在 Photoshop 中&#xff0c;HSB&#xff08;色相、饱和度和明度&#xff09;仅作为表达颜色的一种方式而存在&#xff0c;并不是一种颜色模式。 色相/饱和度命令就是基于色彩三要素进行调色的常用命令。 还有一个与 HSB 相关的滤镜&#xff1a;HSB/HSL 滤镜&#xff0c;用于实…

什么是交互测试?

最近有接触到一个有趣的名词&#xff1a;交互测试。 在对这个名词进行解释之前&#xff0c;我先去特意请教了一个产品经理朋友&#xff0c;问下交互的概念。于是知道了我们的行业里面还有很多个有趣的职位&#xff1a;交互设计师、UE、UI、前端、设计.....等等等等这些&#x…

C语言——查漏补缺

前言 本篇博客主要记录一些C语言的遗漏点&#xff0c;完成查漏补缺的工作&#xff0c;如果读者感兴趣&#xff0c;可以看看下面的内容。都是一些小点&#xff0c;下面进入正文部分。 1. 字符汇聚 编写代码&#xff0c;演示多个字符从两端移动&#xff0c;向中间汇聚 #inclu…

Linux:多线程(三.POSIX信号量、生产消费模型、线程池、其他常见的锁)

上次讲解了&#xff1a;Linux&#xff1a;多线程&#xff08;二.理解pthread_t、线程互斥与同步、基于阻塞队列的生产消费模型&#xff09; 文章目录 1.POSIX信号量1.1引入1.2回顾加深理解信号量1.3信号量的操作接口 2.基于循环队列的生产消费模型2.1循环队列2.2整个项目 3.线程…

网络协议七 应用层 HTTP 协议

应用层常见的协议 HTTP协议 一. 如何查看我们的http 协议全部的内容有哪些呢&#xff1f; 一种合理的方法是 通过 wireshark 软件&#xff0c;找到想要查看的HTTP --->追踪流--->HTTP流 来查看 结果如下&#xff1a;红色部分 为 发送给服务器的&#xff0c;蓝色部分为服…