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

相关推荐
ZYMFZ5 分钟前
python面向对象
前端·数据库·python
长空任鸟飞_阿康10 分钟前
在 Vue 3.5 中优雅地集成 wangEditor,并定制“AI 工具”下拉菜单(总结/润色/翻译)
前端·vue.js·人工智能
lapiii35814 分钟前
快速学完React计划(第一天)
前端·react.js·前端框架
liangshanbo121514 分钟前
React + TypeScript 企业级编码规范指南
ubuntu·react.js·typescript
苏打水com15 分钟前
从 HTML/CSS/JS 到 React:前端进阶的平滑过渡指南
前端·javascript·html
一枚前端小能手25 分钟前
🔐 单点登录还在手动跳转?这几个SSO实现技巧让你的用户体验飞起来
前端·javascript
小潘同学28 分钟前
Vue3中响应式数据深度拷贝 Avoid app logic that relies on,,,,,,,,
前端
六六Leon30 分钟前
Kuikly跨端模式接入资源管理
前端
tianchang33 分钟前
深入理解 JavaScript 异步机制:从语言语义到事件循环的全景图
前端·javascript