useLayoutEffect:你以为它和useEffect是"亲兄弟"?其实差别大了!

大家好,我是小杨。今天想和大家聊聊React中一个容易被误解的Hook------useLayoutEffect。很多人觉得它和useEffect长得像,用起来也差不多,但实际上这对"兄弟"的性格差异可不小!

记得我刚接触useLayoutEffect时,也曾天真地以为:"这不就是个同步版的useEffect吗?"结果在真实项目中踩了几个坑后,才发现事情没那么简单。


一、先来认识一下:useLayoutEffect是什么?

简单来说,useLayoutEffect是useEffect的"急性子兄弟"。它的函数签名和useEffect一模一样,但执行时机却大不相同。

jsx 复制代码
import { useLayoutEffect, useState } from 'react';

function MyComponent() {
  const [width, setWidth] = useState(0);
  
  useLayoutEffect(() => {
    const measuredWidth = document.getElementById('myElement').offsetWidth;
    setWidth(measuredWidth);
  }, []);
  
  return <div id="myElement">我的宽度是:{width}px</div>;
}

看到这里你可能会问:"这用useEffect不也能实现吗?"别急,让我们继续往下看。


二、关键区别:什么时候执行?

这是useLayoutEffect和useEffect最核心的区别:

  • useEffect:异步执行,在浏览器绘制完成后运行
  • useLayoutEffect:同步执行,在浏览器绘制前运行

换句话说,useLayoutEffect会阻塞浏览器的绘制,直到它的回调函数执行完毕。

jsx 复制代码
// 用例:在页面渲染前测量DOM元素
function Tooltip({ content }) {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const tooltipRef = useRef(null);
  
  useLayoutEffect(() => {
    const rect = tooltipRef.current.getBoundingClientRect();
    // 在用户看到之前就计算好位置
    setPosition({
      x: window.innerWidth - rect.width - 10,
      y: 10
    });
  }, [content]);
  
  return (
    <div ref={tooltipRef} style={{ position: 'fixed', left: position.x, top: position.y }}>
      {content}
    </div>
  );
}

三、什么时候该用useLayoutEffect?

经过这些年的实践,我总结出几个useLayoutEffect的典型使用场景:

1. 避免视觉闪烁

当你需要基于DOM测量结果进行渲染时,useLayoutEffect可以避免用户看到中间状态。

jsx 复制代码
function ResizablePanel({ children }) {
  const [size, setSize] = useState(0);
  const containerRef = useRef(null);
  
  useLayoutEffect(() => {
    const containerWidth = containerRef.current.offsetWidth;
    // 在渲染前计算合适的大小
    setSize(containerWidth * 0.8);
  }, []);
  
  return (
    <div ref={containerRef}>
      <div style={{ width: `${size}px` }}>
        {children}
      </div>
    </div>
  );
}

2. 同步布局操作

需要同步读取布局属性并立即基于这些属性进行更新时。

jsx 复制代码
function StickyHeader() {
  const [isSticky, setIsSticky] = useState(false);
  const headerRef = useRef(null);
  
  useLayoutEffect(() => {
    const checkPosition = () => {
      const rect = headerRef.current.getBoundingClientRect();
      setIsSticky(rect.top <= 0);
    };
    
    checkPosition();
    window.addEventListener('scroll', checkPosition);
    
    return () => window.removeEventListener('scroll', checkPosition);
  }, []);
  
  return <header ref={headerRef} className={isSticky ? 'sticky' : ''}>头部</header>;
}

四、我踩过的坑:性能问题

useLayoutEffect虽然强大,但滥用会导致性能问题。我曾经在一个项目中过度使用useLayoutEffect,结果导致了明显的性能下降。

jsx 复制代码
// 错误示范:在useLayoutEffect中做重操作
useLayoutEffect(() => {
  // 这个繁重的计算会阻塞页面渲染!
  const heavyData = processLargeData(props.data);
  setProcessedData(heavyData);
}, [props.data]);

经验法则:除非确实需要在绘制前同步执行操作,否则优先使用useEffect。


五、实用技巧:如何选择?

根据我的经验,选择useEffect还是useLayoutEffect可以遵循这个流程:

  1. 先用useEffect:大多数情况下它都是正确的选择
  2. 如果出现视觉闪烁:考虑切换到useLayoutEffect
  3. 测量DOM布局:需要同步获取布局信息时用useLayoutEffect
  4. 性能敏感:在useLayoutEffect中避免繁重计算

六、总结:兄弟虽像,各有所长

  • useEffect:适用于大多数副作用,不会阻塞渲染
  • useLayoutEffect:适用于需要同步布局测量的场景

记住一个简单的比喻:useEffect就像寄普通快递,不着急;useLayoutEffect就像闪送,必须马上送到!

最后给大家的建议: "不要因为useLayoutEffect听起来很酷就滥用它,大多数时候useEffect才是你的好朋友。"

⭐ 写在最后

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅ 认为我部分代码过于老旧,可以提供新的API或最新语法

✅ 对于文章中部分内容不理解

✅ 解答我文章中一些疑问

✅ 认为某些交互,功能需要优化,发现BUG

✅ 想要添加新功能,对于整体的设计,外观有更好的建议

✅ 一起探讨技术加qq交流群:906392632

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!

相关推荐
智算菩萨3 分钟前
基于spaCy的英文自然语言处理系统:低频词提取与高级文本分析
前端·javascript·easyui
刘一说13 分钟前
Vue单页应用(SPA)开发全解析:从原理到最佳实践
前端·javascript·vue.js
疯狂成瘾者15 分钟前
前端vue核心知识点
前端·javascript·vue.js
Laravel技术社区1 小时前
用PHP8实现斗地主游戏,实现三带一,三带二,四带二,顺子,王炸功能(第二集)
前端·游戏·php
m0_738120722 小时前
应急响应——知攻善防Web-3靶机详细教程
服务器·前端·网络·安全·web安全·php
hh随便起个名8 小时前
力扣二叉树的三种遍历
javascript·数据结构·算法·leetcode
我是小路路呀9 小时前
element级联选择器:已选中一个二级节点,随后又点击了一个一级节点(仅浏览,未确认选择),此时下拉框失去焦点并关闭
javascript·vue.js·elementui
程序员爱钓鱼9 小时前
Node.js 编程实战:文件读写操作
前端·后端·node.js
PineappleCoder9 小时前
工程化必备!SVG 雪碧图的最佳实践:ID 引用 + 缓存友好,无需手动算坐标
前端·性能优化
JIngJaneIL10 小时前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端