useEffect vs useLayoutEffect

到底该用哪个?

先说结论:99% 的情况用 useEffect,只在需要测量 DOM 时用 useLayoutEffect。

执行时机不同

useEffect(异步)

  • 执行时机:浏览器完成绘制之后
  • 特点不阻塞用户看到界面更新
  • 适合:数据请求、埋点、事件订阅

useLayoutEffect(同步)

  • 执行时机:React 更新了 DOM,但浏览器还没绘制之前
  • 特点会阻塞浏览器绘制
  • 适合:防止视觉闪烁

什么时候必须用 useLayoutEffect?

场景:需要测量 DOM 尺寸,然后根据尺寸设置另一个元素的位置。

用 useEffect 的问题

useEffect(() => {
  // 浏览器先画了一次(位置错) -> 执行 effect -> 改位置 -> 浏览器重画(位置对)
  // 用户会看到闪烁!
  const height = divRef.current.offsetHeight;
  otherDiv.style.marginTop = height + 'px';
});

用 useLayoutEffect 的问题

useLayoutEffect(() => {
  // React 改 DOM -> 测量并改位置 -> 浏览器绘制
  // 这一系列是同步的,用户直接看到最终结果,没有闪烁
  const height = divRef.current.offsetHeight;
  otherDiv.style.marginTop = height + 'px';
});

SSR 注意

服务端渲染时:

  • 两个 Hook 都不会执行(正常)
  • useLayoutEffect报警告(因为暗示有 DOM 操作,但服务端没有)

用条件判断规避:

useLayoutEffect(() => {
  if (typeof window !== 'undefined') {
    // DOM 操作
  }
}, []);

总结

Hook时机阻塞用处
useEffect绘制后不阻塞99% 的场景
useLayoutEffect绘制前阻塞DOM 测量、布局修正

记住:能用 useEffect 就用 useEffect,性能更好。