React 中hooks之useLayoutEffect 用法总结以及与useEffect的区别

React useLayoutEffect

1. useLayoutEffect 基本概念

useLayoutEffect 是 React 的一个 Hook,它的函数签名与 useEffect 完全相同,但它会在所有的 DOM 变更之后同步调用 effect。它可以用来读取 DOM 布局并同步触发重渲染。

2. useLayoutEffect vs useEffect

2.1 执行时机对比

Hook 名称执行时机执行方式使用场景
useEffectDOM 更新后且浏览器重新绘制屏幕之后异步执行 (组件渲染完成后)异步执行,不阻塞浏览器渲染大多数副作用,如数据获取、订阅
useLayoutEffectDOM 更新后且浏览器重新绘制屏幕之前同步执行(组件将要渲染时)同步执行,会阻塞浏览器渲染需要同步测量 DOM 或更新布局

2.2 执行顺序示例

function ExampleComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('useEffect 执行'); // 后执行
  });

  useLayoutEffect(() => {
    console.log('useLayoutEffect 执行'); // 先执行
  });

  return (
    <div onClick={() => setCount(c => c + 1)}>
      点击次数:{count}
    </div>
  );
}

3. useLayoutEffect 使用场景

3.1 DOM 测量和更新

function AutoHeight() {
  const [height, setHeight] = useState(0);
  const elementRef = useRef();

  useLayoutEffect(() => {
    // 在 DOM 更新后立即测量高度
    const element = elementRef.current;
    const elementHeight = element.getBoundingClientRect().height;
    
    if (elementHeight !== height) {
      // 立即更新高度,避免闪烁
      setHeight(elementHeight);
    }
  }, [height]);

  return (
    <div>
      <div ref={elementRef} style={{ height: height || 'auto' }}>
        内容
      </div>
      <div>当前高度: {height}px</div>
    </div>
  );
}

3.2 防止闪烁的工具提示

function Tooltip({ text, position }) {
  const tooltipRef = useRef();
  const [tooltipPosition, setTooltipPosition] = useState(position);

  useLayoutEffect(() => {
    const tooltip = tooltipRef.current;
    const rect = tooltip.getBoundingClientRect();
    
    // 检查是否超出视口
    if (rect.right > window.innerWidth) {
      // 立即调整位置,避免闪烁
      setTooltipPosition({
        ...position,
        left: position.left - (rect.right - window.innerWidth)
      });
    }
  }, [position]);

  return (
    <div
      ref={tooltipRef}
      style={{
        position: 'absolute',
        ...tooltipPosition
      }}
    >
      {text}
    </div>
  );
}

3.3 动画处理

function AnimatedComponent() {
  const elementRef = useRef();
  const [isVisible, setIsVisible] = useState(false);

  useLayoutEffect(() => {
    if (isVisible) {
      const element = elementRef.current;
      // 立即设置初始状态
      element.style.opacity = '0';
      element.style.transform = 'translateY(20px)';
      
      // 强制重排
      element.getBoundingClientRect();
      
      // 应用动画
      element.style.transition = 'opacity 0.5s, transform 0.5s';
      element.style.opacity = '1';
      element.style.transform = 'translateY(0)';
    }
  }, [isVisible]);

  return (
    <div>
      <button onClick={() => setIsVisible(true)}>显示</button>
      <div ref={elementRef}>
        动画内容
      </div>
    </div>
  );
}

3.4 滚动位置同步

function ScrollSync({ content }) {
  const scrollContainerRef = useRef();

  useLayoutEffect(() => {
    const element = scrollContainerRef.current;
    // 内容更新后立即滚动到底部
    element.scrollTop = element.scrollHeight;
  }, [content]); // 当内容更新时执行

  return (
    <div 
      ref={scrollContainerRef}
      style={{ 
        height: '200px', 
        overflow: 'auto' 
      }}
    >
      {content.map((item, index) => (
        <div key={index}>{item}</div>
      ))}
    </div>
  );
}

3.5 Modal 定位

function Modal({ isOpen, children }) {
  const modalRef = useRef();

  useLayoutEffect(() => {
    if (isOpen) {
      const modalElement = modalRef.current;
      const viewportHeight = window.innerHeight;
      const modalHeight = modalElement.getBoundingClientRect().height;
      
      // 立即计算并设置最佳位置
      modalElement.style.top = \`\${Math.max(
        0,
        (viewportHeight - modalHeight) / 2
      )}px\`;
    }
  }, [isOpen]);

  if (!isOpen) return null;

  return (
    <div className="modal-overlay">
      <div ref={modalRef} className="modal">
        {children}
      </div>
    </div>
  );
}

4. 性能考虑

4.1 何时使用 useLayoutEffect

  • 需要同步测量 DOM 元素
  • 需要在视觉更新前进行 DOM 修改
  • 需要避免闪烁或布局抖动
  • 处理依赖于 DOM 布局的动画

4.2 何时使用 useEffect

  • 数据获取
  • 订阅事件
  • 日志记录
  • 其他不需要同步 DOM 测量或修改的副作用

5. 最佳实践

  1. 优先使用 useEffect
// ✅ 大多数情况下使用 useEffect 即可
useEffect(() => {
  // 异步操作,不影响渲染
  fetchData();
}, []);
  1. 仅在必要时使用 useLayoutEffect
// ✅ 需要同步 DOM 测量和更新时使用 useLayoutEffect
useLayoutEffect(() => {
  // 同步操作,立即更新 DOM
  updateDOMPosition();
}, []);
  1. 注意性能影响
// ❌ 避免在 useLayoutEffect 中进行耗时操作
useLayoutEffect(() => {
  // 不要在这里进行大量计算或 API 调用
  heavyComputation();
}, []);

// ✅ 耗时操作应该放在 useEffect 中
useEffect(() => {
  heavyComputation();
}, []);
  1. 合理使用依赖项
function OptimizedComponent({ data }) {
  useLayoutEffect(() => {
    // 只在真正需要同步更新的依赖项发生变化时执行
  }, [data.layout]); // 只依赖布局相关的属性
}

6. 注意事项

  1. useLayoutEffect 在服务器端渲染(SSR)中会收到警告,因为它只能在客户端执行
  2. 过度使用 useLayoutEffect 可能会导致性能问题
  3. 应该将耗时的操作放在 useEffect 中,只在 useLayoutEffect 中处理视觉相关的同步更新
  4. 在条件语句中使用时需要注意 Hook 规则

通过合理使用 useLayoutEffect 和 useEffect,我们可以更好地控制副作用的执行时机,优化用户体验,同时保持应用的性能。在实际开发中,应该根据具体场景选择合适的 Hook。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大疆最新款无人机发布,可照亮百米之外目标

近日&#xff0c;DJI 大疆发布全新小型智能多光旗舰 DJI Matrice 4 系列&#xff0c;包含 Matrice 4T 和 Matrice 4E 两款机型。DJI Matrice 4E 价格为27888 元起&#xff0c;DJI Matrice 4T价格为38888元起。 图片来源&#xff1a;大疆官网 DJI Matrice 4E DJI Matrice 4T D…

基于Java的语音陪聊软件——支持聊天私聊-礼物系统-直播系统-缘分匹配-游戏陪玩

丰富的经验、成熟的技术&#xff0c;打造适合当下市场发展的语音交友软件源码。Java 语言凭借其独特的优势&#xff0c;为这款语音陪聊软件的稳健运行和持续发展奠定了坚实基础。它不仅融合了聊天私聊、礼物系统和直播系统等实用且有趣的功能&#xff0c;还创新性地引入了缘分匹…

npm发布工具包+使用

1.初始化package包 npm init -y {"name": "common-cjs-tools","version": "1.0.0","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" &&…

WXML模版语法-事件绑定

知识点1&#xff1a;什么是事件 事件是渲染层到逻辑层的通讯方式。通过事件可以将用户在渲染层产生的行为&#xff0c;反馈到逻辑层进行业务的处理。 知识点2&#xff1a;小程序中常用的事件 类型绑定方式事件描述tapbindtap或bind:tap手指触摸后马上离开&#xff0c;类似于…

Uniapp-运行到手机安卓基座报错

1、运行报错 2、解决adb冲突,显示设备列表&#xff0c;说明手机与电脑连接成功 3、重新运行&#xff0c;还是找不到&#xff0c;就多刷新几下即可。

ESP8266固件烧录

一、烧录原理 1、引脚布局 2、引脚定义 3、尺寸封装 4、环境要求 5、接线方式 ESP8266系列模块集成了高速GPI0和外围接口&#xff0c;这可能会导致严重的开关噪声。如果某些应用需要高功率和EMI特性&#xff0c;建议在数字I/0线上串联10到100欧姆。这可以在切换电源时抑制过冲…

紫光无人机AI飞控平台介绍

随着无人机技术的迅猛发展&#xff0c;无人机飞控平台的智能化需求不断提升。紫光无人机AI飞控平台作为一款创新型产品&#xff0c;为用户提供了从飞行控制到任务管理的一站式解决方案&#xff0c;尤其在AI实时识别和事件分析方面具有显著优势。本文将介绍平台的核心功能、技术…

【SpringBoot】Spring 一站式解决方案:融合统一返回结果、异常处理与适配器模式

前言 &#x1f31f;&#x1f31f;本期讲解关于统一功能处理的详细介绍~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话…

回归预测 | MATLAB实RVM相关向量机多输入单输出回归预测

回归预测 | MATLAB实RVM相关向量机多输入单输出回归预测 目录 回归预测 | MATLAB实RVM相关向量机多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 RVM-Adaboost相关向量机集成学习多输入单输出回归预测是一种先进的机器学习方法&#xff0c;用于处理…

【C语言】内存函数详解与模拟实现

文章目录 拓展&#xff1a;Ⅰ. memcpy -- 内存拷贝1、函数介绍与使用2、模拟实现 Ⅱ. memmove -- 内存拷贝1、函数介绍与使用&#xff08;与memcpy函数的区别&#xff09;2、模拟实现 Ⅲ. memcmp -- 内存比较1、函数介绍与使用2、模拟实现 Ⅳ. memset -- 内存设置1、函数介绍与…

解析OVN架构及其在OpenStack中的集成

引言 随着云计算技术的发展&#xff0c;虚拟化网络成为云平台不可或缺的一部分。为了更好地管理和控制虚拟网络&#xff0c;Open Virtual Network (OVN) 应运而生。作为Open vSwitch (OVS) 的扩展&#xff0c;OVN 提供了对虚拟网络抽象的支持&#xff0c;使得大规模部署和管理…

解密AIGC三大核心算法:GAN、Transformer、Diffusion Models原理与应用

在当今数字化时代&#xff0c;人工智能生成内容&#xff08;AIGC&#xff09;技术正以前所未有的速度改变着我们的生活和工作方式。从创意无限的文本生成&#xff0c;到栩栩如生的图像创作&#xff0c;再到动听的音乐旋律&#xff0c;AIGC的魔力无处不在。而这一切的背后&#…

艾体宝干货丨网络故障排除基本指南

一、确保网络可视性以有效排除故障 有效的网络故障排除要求对穿越网络的数据具有完全的可见性&#xff0c;以便迅速识别和解决潜在问题。本指南深入探讨了一种结构化的网络分析方法&#xff0c;旨在提高故障排除的效率。首先&#xff0c;提出正确的问题至关重要&#xff0c;它…

汽车免拆诊断案例 | 2007 款法拉利 599 GTB 车发动机故障灯异常点亮

故障现象  一辆2007款法拉利599 GTB车&#xff0c;搭载6.0 L V12自然吸气发动机&#xff08;图1&#xff09;&#xff0c;累计行驶里程约为6万km。该车因发动机故障灯异常点亮进厂检修。 图1 发动机的布置 故障诊断 接车后试车&#xff0c;发动机怠速轻微抖动&#xff0c;…

浪潮海岳 UploadListFile文件上传致RCE漏洞

一、漏洞简介 浪潮云财务系统的/cwbase/EP/ListContent/UploadListFile.ashx接口存在任意文件上传漏洞&#xff0c;未经身份验证的攻击者可以通过该漏洞上传恶意脚本文件&#xff0c;从而控制目标服务器。 二、漏洞影响 三、网络测绘&#xff1a; fofa: body"/cwbase/w…

高等数学学习笔记 ☞ 不定积分的积分法

1. 第一换元积分法 1. 基础概念&#xff1a;形如的过程&#xff0c;称为第一换元积分法。 2. 核心思想&#xff1a;通过对被积函数的观察(把被积函数的形式与积分表的积分公式进行比较)&#xff0c;把外部的部分项拿到的内部(求原函数)&#xff0c; 然后进行拼凑&#xff0c;…

Spring Boot 整合 Shiro详解

文章目录 Spring Boot 整合 Shiro详解一、引言二、整合步骤1、创建项目并引入依赖2、配置Shiro2.1、自定义Realm2.2、配置SecurityManager和ShiroFilterFactoryBean 三、使用示例四、总结 Spring Boot 整合 Shiro详解 一、引言 在现代的Web应用开发中&#xff0c;用户认证和授…

win10 Outlook(new) 企业邮箱登录 登录失败。请在几分钟后重试。附移除办法

windows系统经常弹出使用Outlook(new&#xff09;&#xff0c;自动切过去。 但是登录企业的内网邮箱&#xff0c;折腾了好几次都使用不了。排查网络等问题&#xff0c;在社区找到了答案。 推出一年多不支持企业账户&#xff0c;所以之前的折腾都是浪费时间。 因为这个答案不太…

tomcat状态一直是Exited (1)

docker run -di -p 80:8080 --nametomcat001 你的仓库地址/tomcat:9执行此命令后tomcat一直是Exited(1)状态 解决办法&#xff1a; 用以下命令创建运行 docker run -it --name tomcat001 -p 80:8080 -d 你的仓库地址/tomcat:9 /bin/bash最终结果 tomcat成功启动

Golang Gin系列-1:Gin 框架总体概述

本文介绍了Gin框架&#xff0c;探索了它的关键特性&#xff0c;并建立了简单入门的应用程序。在这系列教程里&#xff0c;我们会探索Gin的主要特性&#xff0c;如路由、中间件、数据库集成等&#xff0c;最终能使用Gin框架构建健壮的web应用程序。 总体概述 Gin是Go编程语言的…