useLayoutEffectuseEffect 类似,与 useEffect 在浏览器 layout 和 painting 完成后异步执行 effect 不同的是,它会在浏览器布局 layout 之后,painting 之前同步执行 effect

useLayoutEffect 的执行时机对比如下:

import React, { useState, useEffect, useLayoutEffect } from 'react';

export default function LayoutEffect() {
  const [width, setWidth] = useState('100px');

  // useEffect 会在所有 DOM 渲染完成后执行 effect 回调
  useEffect(() => {
    console.log('effect width: ', width);
  });
  // useLayoutEffect 会在所有的 DOM 变更之后同步执行 effect 回调
  useLayoutEffect(() => {
    console.log('layoutEffect width: ', width);
  });
  
  return (
    <>
      <div id='content' style={{ width, background: 'red' }}>内容</div>
      <button onClick={() => setWidth('100px')}>100px</button>
      <button onClick={() => setWidth('200px')}>200px</button>
      <button onClick={() => setWidth('300px')}>300px</button>
    </>
  );
}

// 使用 setTimeout 保证在组件第一次渲染完成后执行,获取到对应的 DOM
setTimeout(() => {
  const contentEl = document.getElementById('content');
  // 监视目标 DOM 结构变更,会在 useLayoutEffect 回调执行后,useEffect 回调执行前调用
  const observer = new MutationObserver(() => {
    console.log('content element layout updated');
  });
  observer.observe(contentEl, {
    attributes: true
  });
}, 1000);


参考: https://juejin.cn/post/7041551402048421901