为什么你需要 useLayoutEffect?深入理解同步副作用

为什么你需要useLayoutEffect?深入理解同步副作用

在 React 的开发中,我们经常使用 useEffect 来处理副作用(如数据获取、订阅、手动 DOM 操作等)。然而,在某些特定的场景下,我们需要在 DOM 更新之后浏览器绘制之前 立即执行一些同步操作,这时就需要使用另一个 Hook ------ useLayoutEffect

一、useLayoutEffect 是什么?

useLayoutEffect 是 React 提供的一个 Hook,用于在组件渲染完成后执行副作用逻辑,但它会在浏览器重新绘制屏幕之前同步执行。这意味这你可以在页面未渲染之前就获取某个DOM节点的状态。

它的用法与 useEffect 几乎完全一致:

jsx 复制代码
useLayoutEffect(() => {
  // 同步执行的副作用逻辑
}, [依赖项]);

不同之处在于执行时机:

  • useEffect:异步执行,在浏览器完成绘制后执行。
  • useLayoutEffect:同步执行,在 DOM 更新后、页面绘制前执行。

二、为什么需要 useLayoutEffect

React 的设计理念是异步和非阻塞的,大多数副作用都可以延迟到绘制之后执行。但在以下几种情况下,必须确保副作用在绘制前完成

  1. 需要读取或修改 DOM 布局信息(如尺寸、位置)

    例如你希望根据某个元素的实际大小来调整布局,如果使用 useEffect,可能会导致视觉上的闪烁或重绘抖动。这是因为useEffect是在页面渲染之后执行的,此时页面已经完成渲染并呈现给用户,这个时候useEffect执行修改样式的操作就会导致样式的快速变化,页面进行再一次的回流重绘从而造成闪烁或抖动

    jsx 复制代码
    useLayoutEffect(() => {
      const height = ref.current?.offsetHeight;
      if (height > 100) {
        setExpanded(false);
      }
    }, []);
  2. 避免视觉抖动(Layout Thrashing)

    如果你在多个异步副作用中反复读写 DOM 尺寸,可能导致多次强制同步布局(Forced Synchronous Layout),影响性能和用户体验。

    useLayoutEffect 可以确保所有计算在绘制前完成,避免这种问题。

三、useLayoutEffectuseEffect 的区别总结

特性 useEffect useLayoutEffect
执行时机 渲染完成后异步执行(绘制后) 渲染完成后同步执行(绘制前)
是否阻塞绘制 是(可能影响性能)
使用频率 推荐优先使用 仅在必要时使用
适合场景 数据请求、事件监听、非关键 DOM 操作 精确测量 DOM、防止布局抖动

四、实际使用场景示例

场景一:根据 DOM 尺寸进行布局调整

jsx 复制代码
const MeasureBox = () => {
  const boxRef = useRef(null);
  const [size, setSize] = useState({ width: 0, height: 0 });

  useLayoutEffect(() => {
    if (boxRef.current) {
      const { offsetWidth, offsetHeight } = boxRef.current;
      setSize({ width: offsetWidth, height: offsetHeight });
    }
  }, []);

  return (
    <div>
      <div ref={boxRef} style={{ width: '200px', height: '100px', background: 'lightblue' }}>
        Box
      </div>
      <p>Width: {size.width}, Height: {size.height}</p>
    </div>
  );
};

场景二:实现精确动画起始状态

在进入动画前,你需要先获取 DOM 的初始位置,并设置动画起点:

jsx 复制代码
useLayoutEffect(() => {
  const element = ref.current;
  if (element) {
    const initialPosition = element.getBoundingClientRect().top;
    animate(element, { top: initialPosition + 100 });
  }
}, []);

五、注意事项

**推荐优先使用 useEffect,**除非你明确需要在绘制前执行副作用,否则应优先使用 useEffect,因为它不会阻塞浏览器绘制,对性能更友好。

**避免在 useLayoutEffect 中执行耗时操作,**由于它是同步执行的,如果在里面做大量计算或网络请求,会显著延长页面渲染时间,造成"白屏"或"卡顿"。

在 SSR(服务端渲染)中需谨慎使用,useLayoutEffect 在服务端无法执行,因为没有 DOM。如果你在 Next.js 或其他 SSR 框架中使用它,务必确保依赖的数据在客户端可用,或者在 useEffect 中降级处理。

相关推荐
AlienZHOU2 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
Captaincc5 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师7 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen7 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒7 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
前端snow8 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林8188 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器
JamesZhang800788 小时前
页面内存只涨不跌? 一次泄漏排查, 牵出 WeakMap 的诞生
前端
Z小明8 小时前
第 6 章 组件进阶
前端·vue.js
江华森8 小时前
HTTP请求的完整过程详解:从DNS解析到TCP挥手的微秒级实战分析
前端