为什么你需要 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 中降级处理。

相关推荐
丘山子22 分钟前
如何规避 A/B Testing 中的致命错误?何时进行 A/B 测试?
前端·后端·面试
打妖妖灵滴哪吒26 分钟前
web端-登录页面验证码的实现(springboot+vue前后端分离)超详细
前端
胡斌附体42 分钟前
小程序难调的组件
前端·小程序·apache·datepicker·自定义组件·checkbox
Mintopia1 小时前
AIGC Claude(Anthropic)接入与应用实战:从字节流到智能交互的奇妙旅程
前端·javascript·aigc
Mintopia1 小时前
Next.js 样式魔法指南:CSS Modules 与 Tailwind CSS 实战
前端·javascript·next.js
用户21411832636021 小时前
dify案例分享-解锁 AI 搜索新玩法:Dify 秘塔搜索工作流搭建教程与效果展示
前端
Stefan的技术笔记1 小时前
LangChain入门指南:5大核心组件解析,快速上手AI应用开发!
前端·langchain
悟空和大王1 小时前
video标签自定义控制按钮--全屏与非全屏--播放与暂停
前端
excel1 小时前
javascript 简介
前端
国家不保护废物1 小时前
跨域问题:从同源策略到JSONP、CORS实战,前端必知必会
前端·javascript·面试