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

news2025/1/17 13:33:56

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。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Django框架:python web开发

1.环境搭建&#xff1a; &#xff08;a&#xff09;开发环境&#xff1a;pycharm &#xff08;b&#xff09;虚拟环境&#xff08;可有可无&#xff0c;优点&#xff1a;使用虚拟环境可以把使用的包自动生成一个文件&#xff0c;其他人需要使用时可以直接选择导入包&#xff…

2024最新版JavaScript逆向爬虫教程-------基础篇之Chrome开发者工具学习

目录 一、打开Chrome DevTools的三种方式二、Elements元素面板三、Console控制台面板四、Sources面板五、Network面板六、Application面板七、逆向调试技巧 7.1 善用搜索7.2 查看请求调用堆栈7.3 XHR 请求断点7.4 Console 插桩7.5 堆内存函数调用7.6 复制Console面板输出 工…

联通用户管理系统(一)

#联通用户管理系统&#xff08;一&#xff09; 1.新建项目 如果你是windows的话&#xff0c;界面应该是如下的&#xff1a; 2.创建app python manage.py startapp app01一般情况下&#xff1a;我们是在pycharm的终端中运行上述指令&#xff0c;但是pychrm中为我们提供了工具…

【网络编程】基础知识

目录 网络发展史 局域网和广域网 局域网&#xff08;LAN&#xff09; 广域网&#xff08;Wan&#xff09; 光猫 路由器 网线 设备通信的要素 IP地址 基本概念 地址划分 特殊地址&#xff08;后续编程使用&#xff09; IP地址转换 字节序 网络模型 网络的体系结…

生产环境中常用的设计模式

生产环境中常用的设计模式 设计模式目的使用场景示例单例模式保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点- 日志记录器- 配置管理器工厂方法模式定义一个创建对象的接口&#xff0c;让子类决定实例化哪个类- 各种工厂类&#xff08;如视频游戏工厂模式创…

C#读写ini配置文件保存设置参数

本示例使用设备&#xff1a;https://item.taobao.com/item.htm?spma21dvs.23580594.0.0.52de2c1b5P5rkA&ftt&id22173428704 [DllImport("kernel32", CharSet CharSet.Unicode)] public static extern uint GetPrivateProfileString(string lpAppName, stri…

C# .NetCore 使用 Flurl.Http 与 HttpClient 请求处理流式响应

AI对话接口采用流式返回&#xff1a; 1、使用Flurl处理返回的数据流 using Flurl; using Flurl.Http; [HttpPost] public async Task<string> GetLiushiChatLaw() { //1、请求参数&#xff0c;根据实际情况 YourModel request new YourModel();string allStr …

ResNet (Residual Network) - 残差网络:深度卷积神经网络的突破

一、引言 在计算机视觉领域&#xff0c;图像识别一直是一个核心且具有挑战性的任务。随着深度学习的发展&#xff0c;卷积神经网络&#xff08;CNN&#xff09;在图像识别方面取得了显著的成果。然而&#xff0c;随着网络深度的增加&#xff0c;出现了梯度消失或梯度爆炸等问题…

C#--在多线程中使用任务并行库(TPL)--15

目录 一.任务并行库的概念以及定义 二.主要特性 三.代码使用示例 1.最基础的Parallel.For使用方式 2.使用 ParallelOptions 来控制并行执行 3.Parallel.ForEach的使用(用于处理集合) 4.带有本地变量的并行循环(用于需要累加或统计的场景) 5.结合Task和Parallel的高级示…

python 寻找数据拐点

import numpy as np import cv2 from scipy.signal import find_peaks# 示例数据 y_data [365.63258786, 318.34824281, 258.28434505, 228.8913738, 190.87220447, 158.28434505, 129.53035144, 111.95846645, 111.95846645, 120.26517572, 140.71246006, 161.79872204, 180.…

论文笔记-arXiv2025-A survey about Cold Start Recommendation

论文笔记-arXiv2025-Cold-Start Recommendation towards the Era of Large Language Models: A Comprehensive Survey and Roadmap 面向大语言模型&#xff08;LLMs&#xff09;时代的冷启动推荐&#xff1a;全面调研与路线图1.引言2.前言3.内容特征3.1数据不完整学习3.1.1鲁棒…

设计模式03:行为型设计模式之策略模式的使用情景及其基础Demo

1.策略模式 好处&#xff1a;动态切换算法或行为场景&#xff1a;实现同一功能用到不同的算法时和简单工厂对比&#xff1a;简单工厂是通过参数创建对象&#xff0c;调用同一个方法&#xff08;实现细节不同&#xff09;&#xff1b;策略模式是上下文切换对象&#xff0c;调用…

飞机电气系统技术分析:数字样机技术引领创新

现代飞机正向着更安全、环保和经济的方向发展&#xff0c;飞机系统的设计日益复杂&#xff0c;对各子系统的性能和可靠性也提出了更高要求。作为飞机的重要组成部分&#xff0c;电气系统&#xff08;Electrical System&#xff0c;ES&#xff09;不仅负责为各类机载设备提供稳定…

(01)FreeRTOS移植到STM32

一、以STM32的裸机工程模板 任意模板即可 二、去官网上下载FreeRTOS V9.0.0 源码 在移植之前&#xff0c;我们首先要获取到 FreeRTOS 的官方的源码包。这里我们提供两个下载 链 接 &#xff0c; 一 个 是 官 网 &#xff1a; http://www.freertos.org/ &#xff0c; 另…

【Unity-Game4Automation PRO 插件】

Game4Automation PRO 插件 是一个用于 Unity 引擎 的工业自动化仿真工具&#xff0c;它提供了对工业自动化领域的仿真和虚拟调试支持&#xff0c;特别是在与工业机器人、生产线、PLC 系统的集成方面。该插件旨在将工业自动化的实时仿真与游戏开发的高质量 3D 可视化能力结合起来…

element select 绑定一个对象{}

背景&#xff1a; select组件的使用&#xff0c;适用广泛的基础单选 v-model 的值为当前被选中的 el-option 的 value 属性值。但是我们这里想绑定一个对象&#xff0c;一个el-option对应的对象。 <el-select v-model"state.form.modelA" …

mybatis延迟加载、缓存

目录 一、所需表 二、延迟加载 1.延迟加载概念 2.立即加载和延迟加载的应用场景 3.多对一延迟加载查询演示 (1)实体类 User Account (2)AccountMapper接口 (3)AccountMapper.xml (4)UserMapper接口 (5)UserMapper.xml (6)在总配置文件(mybatis-config.xml)中开启延…

VIVADO FIFO (同步和异步) IP 核详细使用配置步骤

VIVADO FIFO (同步和异步) IP 核详细使用配置步骤 目录 前言 一、同步FIFO的使用 1、配置 2、仿真 二、异步FIFO的使用 1、配置 2、仿真 前言 在系统设计中&#xff0c;利用FIFO&#xff08;first in first out&#xff09;进行数据处理是再普遍不过的应用了&#xff0c…

一、1-2 5G-A通感融合基站产品及开通

1、通感融合定义和场景&#xff08;阅读&#xff09; 1.1通感融合定义 1.2通感融合应用场景 2、通感融合架构和原理&#xff08;较难&#xff0c;理解即可&#xff09; 2.1 感知方式 2.2 通感融合架构 SF&#xff08;Sensing Function&#xff09;&#xff1a;核心网感知控制…

某政务行业基于 SeaTunnel 探索数据集成平台的架构实践

分享嘉宾&#xff1a;某政务公司大数据技术经理 孟小鹏 编辑整理&#xff1a;白鲸开源 曾辉 导读&#xff1a;本篇文章将从数据集成的基础概念入手&#xff0c;解析数据割裂给企业带来的挑战&#xff0c;阐述数据集成的重要性&#xff0c;并对常见的集成场景与工具进行阐述&…