当前位置: 首页 > news >正文

带你彻底搞懂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>);
}

效果如下

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

相关文章:

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

开篇第一句: useLayoutEffect 可能会影响性能。尽可能使用 useEffect。 useLayoutEffect 是 useEffect 的一个版本&#xff0c;在浏览器重新绘制屏幕之前触发。 使用方法 useLayoutEffect(setup, dependencies?)调用 useLayoutEffect 在浏览器重新绘制屏幕之前进行布局测量&…...

大厂进阶之二:React高级用法HOC、Hooks对比、异步组件

本文分文三部分&#xff1a; HOC高阶组件 higher order componentHooks 16.8版本后新增的钩子API异步组件使用lazy和suspense两个api实现组件代码打包分割和异步加载 一、HOC高阶组件 1、定义 高阶组件不是组件而是函数&#xff0c;是react中用于复用组件逻辑的高级技巧&am…...

【扒代码】ope.py

文件目录&#xff1a; 引用方式 if not self.zero_shot: # 非零样本情况下&#xff0c;计算边界框的宽度和高度 box_hw torch.zeros(bboxes.size(0), bboxes.size(1), 2).to(bboxes.device) box_hw[:, :, 0] bboxes[:, :, 2] - bboxes[:, :, 0] # 宽度 box_hw[:, :, 1] bbox…...

【Rust光年纪】探索Rust终端编程:从跨平台操作到用户界面设计

构建跨平台终端应用的完美选择&#xff1a;Rust 库综述 前言 随着终端应用程序的发展&#xff0c;越来越多的开发者开始寻找跨平台的、易于使用的库来构建终端用户界面和执行终端操作。本文将介绍几个流行的 Rust 库&#xff0c;它们提供了丰富的功能和灵活的 API 来满足不同…...

67、ceph

一、ceph 1.1、ceph概念 ceph是一个开源的&#xff0c;用c语言写的分布式的存储系统。存储文件数据。 /dev/sdb fdisk /dev/sdb gdisk /dev/sdb lvm 逻辑卷 可以扩容 raid 磁盘阵列 高可用 基于物理意义上的单机的存储系统。 分布式有多台物理磁盘组成一个集群&…...

最大正方形[中等]

优质博文&#xff1a;IT-BLOG-CN 一、题目 在一个由0和1组成的二维矩阵内&#xff0c;找到只包含1的最大正方形&#xff0c;并返回其面积。 示例 1&#xff1a; 输入&#xff1a;matrix [["1","0","1","0","0"],[&quo…...

JavaScript 浅谈观察者模式 前端设计模式

2、观察者模式 2.1、观察者模式 2.1.1、前言 定义一种一对多的依赖关系&#xff0c;当一个对象发生变化时&#xff0c;所有依赖于它的对象都会自动收到通知并更新。 两个角色&#xff1a; Subject&#xff08;主题/被观察者&#xff09; Observer&#xff08;观察者&…...

【自动驾驶】自定义消息格式的话题通信(C++版本)

目录 新建消息文件更改包xml文件中的依赖关系更改cmakelist文件中的配置执行时依赖改变cmakelist编译顺序发布者程序调用者程序新建launch文件程序测试 新建消息文件 在功能包目录下&#xff0c;新建msg文件夹&#xff0c;下面新建mymsg.msg文件&#xff0c;其内容为 string …...

提升前端性能的JavaScript技巧

1. 前端JavaScript性能问题 前端JavaScript的性能问题可以显著影响Web应用的用户体验和整体性能。以下是一些常见的前端JavaScript性能问题: 1.1. 频繁的DOM操作 问题描述:JavaScript经常需要与DOM(文档对象模型)交互来更新页面内容。然而,每次DOM操作都可能触发浏览器的…...

“服务之巅:Spring Cloud中SLA监控与管理的艺术“

标题&#xff1a;“服务之巅&#xff1a;Spring Cloud中SLA监控与管理的艺术” 在微服务架构中&#xff0c;服务调用的可靠性和性能是至关重要的。服务级别协议&#xff08;Service Level Agreement&#xff0c;简称SLA&#xff09;是衡量服务性能的关键指标&#xff0c;它定义…...

ChatGPT角色定位提问提示词和指令完整版

角色定位提问 在与ChatGPT的对话中&#xff0c;角色定位提问是一种有效的策略&#xff0c;通过为ChatGPT和自己设定特定的角色或身份&#xff0c;可以引导对话朝着更加具体、有针对性的方向发展。这种提问方式不仅有助于ChatGPT更好地理解问题的背景和需求&#xff0c;还能使回…...

docker之我不会的命令

docker命令之我不会的 保存镜像&#xff08;打包&#xff09; docker save 镜像名或镜像id -o 保存路径和镜像名字例子&#xff1a; docker save tomcat -o /home/my_tomcat.tar加载保存的镜像 docker load -i 镜像保存的位置例子 在/home/路径下 docker load -i my_tomca…...

Together规则引擎 金融解决方案

目录 1.金融法规和期望正在发生变化,快速跟踪您的金融数字化变革&#xff01;2.抵押贷款功能集&#xff08;MFS&#xff09;3.MFS 示例模型4.MFS 知识特点5.MFS特定功能 1.金融法规和期望正在发生变化,快速跟踪您的金融数字化变革&#xff01; ogether规则引擎使金融机构能够简…...

【PyQt5】PyQt5 主要类

1.经常使用的模块 Sr.No.模块描述1QtCore其他模块使用的核心非GUI类2QtGui图形用户界面组件3QtMultimedia低级多媒体编程的类4QtNetwork网络编程的类5QtOpenGLOpenGL支持类6QtScript用于评估Qt脚本的类7QtSql使用SQL进行数据库集成的类8QtSvg用于显示SVG文件内容的类9QtWebKit…...

渗透测试实战-HFS远程RCE漏洞利用

免责声明&#xff1a;文章来源于真实渗透测试&#xff0c;已获得授权&#xff0c;且关键信息已经打码处理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本…...

企业级管理系统模板 -- 若依

文章目录 前言一、若依模板运行效果二、若依模板下载地址 1、版本说明2、前端下载地址3、后端下载地址三、修改模板代码名称四、修改前端标题及logo总结 前言 在我们学习别人的项目时&#xff0c;总会遇到许多不同的管理系统&#xff0c;例如&#xff1a;学生管理系统&#xf…...

无人车搭载无人机技术详解

无人车搭载无人机技术&#xff0c;是近年来智能交通与无人机技术深度融合的产物&#xff0c;旨在通过集成两者的优势&#xff0c;实现更加灵活、高效的作业能力。该技术将无人机作为无人车的一个可移动、多功能的传感器平台或执行器&#xff0c;通过协同工作&#xff0c;扩展无…...

从“抠图”到“抠视频”,Meta上新AI工具SAM 2。

继2023年4月首次推出SAM&#xff0c;实现对图像的精准分割后&#xff0c;Meta于北京时间2024年7月30日推出了能够分割视频的新模型SAM 2&#xff08;Segment Anything Model 2&#xff09;。SAM 2将图像分割和视频分割功能整合到一个模型中。所谓“分割”&#xff0c;是指区别视…...

一篇讲清楚什么是密码加密和加盐算法 | 附Java代码实现

目录 前言&#xff1a; 一、密码加密 1. MD5介绍 2.彩虹表攻击 3.测试复杂密码是否能被攻破 二、加盐算法 1.对密码123456演示加盐算法 2.盐值的储存 3.密码加盐思想总结 三、Java代码实现 前言&#xff1a; 早些年&#xff0c;数据泄露屡见不鲜&#xff0c;每个班上总…...

C++入门2

函数重载 函数重载&#xff1a;是函数的一种特殊情况&#xff0c;C允许在同一作用域中声明几个功能类似的同名函数&#xff0c;这 些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同&#xff0c;常用来处理实现功能类似数据类型 不同的问题 比如下面的 int add(int x…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

10-Oracle 23 ai Vector Search 概述和参数

一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI&#xff0c;使用客户端或是内部自己搭建集成大模型的终端&#xff0c;加速与大型语言模型&#xff08;LLM&#xff09;的结合&#xff0c;同时使用检索增强生成&#xff08;Retrieval Augmented Generation &#…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

离线语音识别方案分析

随着人工智能技术的不断发展&#xff0c;语音识别技术也得到了广泛的应用&#xff0c;从智能家居到车载系统&#xff0c;语音识别正在改变我们与设备的交互方式。尤其是离线语音识别&#xff0c;由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力&#xff0c;广…...