useLayoutEffect 在浏览器绘制之前同步执行,用于 DOM 测量和同步更新。

useLayoutEffect Hook 详解

useLayoutEffect 在浏览器绘制之前同步执行,用于需要同步读取或修改 DOM 布局的场景。

// 与 useEffect 签名相同

useLayoutEffect(() => {
  // 在绘制前同步执行
  const rect = ref.current.getBoundingClientRect();
  setPosition({ x: rect.left, y: rect.top });

  return () => {
    // 清理函数
  };
}, [dependencies]);

示例 1: useEffect vs useLayoutEffect

useEffect(可能闪烁)

useLayoutEffect(无闪烁)

说明: 快速多次点击按钮,观察两个方块的行为差异。 useEffect 版本可能会先显示在初始位置再跳转,而 useLayoutEffect 版本直接显示在正确位置。

示例 2: DOM 测量

少量内容

测量到的尺寸:0 x 0 px

关键点: useLayoutEffect 在浏览器绘制前同步执行, 所以当内容变化时,我们能立即获取到新的尺寸,而不是旧的尺寸。

示例 3: Tooltip 定位

为什么用 useLayoutEffect?

  • 需要测量 button 和 tooltip 的位置
  • 在绘制前计算正确位置,避免 tooltip 先出现在错误位置再跳转
  • 确保用户看到的是正确定位的 tooltip

示例 4: 自动调整高度的文本框

实现原理:
  1. 内容变化时触发 useLayoutEffect
  2. 先将高度设为 auto 重置
  3. 读取 scrollHeight 获取内容实际高度
  4. 将高度设置为 scrollHeight
  5. 由于是 useLayoutEffect,用户看不到闪烁

示例 5: 执行顺序

点击“触发渲染”查看执行顺序...

执行顺序:

  1. 组件渲染
  2. DOM 更新
  3. useLayoutEffect 同步执行
  4. 浏览器绘制
  5. useEffect 异步执行

使用指南

✅ 使用 useLayoutEffect

  • 测量 DOM 元素尺寸/位置
  • Tooltip/Dropdown 定位
  • 防止视觉闪烁
  • 动画初始状态设置

❌ 使用 useEffect

  • 数据获取
  • 事件订阅
  • 日志记录
  • 大多数副作用