一、核心定义与作用
useLayoutEffect 是 React 提供的一个副作用 Hook,它的核心作用是在 DOM 更新之后、浏览器绘制(paint)之前 同步执行副作用代码。
可以把它理解为:React 先更新了 DOM 结构,还没来得及把更新后的界面画到屏幕上,就先执行 useLayoutEffect 里的代码,执行完后再让浏览器渲染画面。这个过程是同步阻塞的,会等代码执行完才继续绘制。
它的语法和 useEffect 完全一致:
jsx
import { useLayoutEffect, useState } from 'react';
function MyComponent() {
const [width, setWidth] = useState(0);
useLayoutEffect(() => {
// 副作用代码:读取/修改 DOM 样式、计算 DOM 尺寸等
const div = document.getElementById('my-div');
if (div) {
setWidth(div.offsetWidth); // 同步获取 DOM 尺寸
}
// 清理函数(可选)
return () => {
// 组件卸载/依赖变化时执行
};
}, []); // 依赖数组(和 useEffect 规则一致)
return <div id="my-div">宽度:{width}</div>;
}
二、与 useEffect 的核心区别(关键)
这是理解 useLayoutEffect 的核心,两者的差异主要在执行时机和阻塞性:
| 特性 | useEffect | useLayoutEffect |
|---|---|---|
| 执行时机 | DOM 更新后 → 浏览器绘制后执行 | DOM 更新后 → 浏览器绘制前执行 |
| 执行方式 | 异步执行(不阻塞浏览器绘制) | 同步执行(阻塞浏览器绘制) |
| 视觉体验 | 可能出现“闪屏”(先绘制约错样式,再修正) | 无闪屏(绘制前修正样式,用户看不到中间态) |
| 适用场景 | 数据请求、订阅、非 DOM 相关副作用 | 读取/修改 DOM 布局、同步调整样式 |
直观示例:对比两者的执行效果
jsx
import { useEffect, useLayoutEffect, useState } from 'react';
function LayoutEffectDemo() {
const [color, setColor] = useState('red');
// useEffect:绘制后执行,会先看到红色,再闪成蓝色
// useEffect(() => {
// setColor('blue');
// }, []);
// useLayoutEffect:绘制前执行,直接看到蓝色,无闪屏
useLayoutEffect(() => {
setColor('blue');
}, []);
return <div style={{ width: 100, height: 100, backgroundColor: color }}></div>;
}
三、适用场景
只有当你需要同步读取/修改 DOM 布局,且不想让用户看到中间态时,才需要用 useLayoutEffect,常见场景:
- 计算 DOM 尺寸/位置:比如获取元素的 offsetWidth、scrollTop,同步更新状态(避免 useEffect 导致的“先渲染错的尺寸,再修正”);
- 调整 DOM 样式:比如根据元素位置动态设置定位、避免页面闪烁;
- 同步操作 DOM:比如在 DOM 更新后立即修改元素的属性,确保用户看不到过渡状态。
四、使用注意事项
- 避免耗时操作:
useLayoutEffect是同步阻塞的,里面的代码如果耗时(比如循环、复杂计算),会直接卡住浏览器,导致页面加载/交互卡顿。耗时逻辑请放到useEffect或 Web Worker 中; - 服务端渲染 (SSR) 注意:在服务端渲染时,
useLayoutEffect不会执行(因为服务端没有 DOM 绘制),如果代码依赖它,可能导致客户端和服务端渲染不一致。解决方法:要么改用useEffect,要么加判断(比如typeof window !== 'undefined'); - 依赖数组规则:和
useEffect完全一致,依赖数组为空则只执行一次(组件挂载后),依赖变化时重新执行,清理函数在组件卸载/依赖变化前执行; - 优先用 useEffect:大部分场景下
useEffect足够用,且不会阻塞渲染,只有确需同步操作 DOM 布局时,才用useLayoutEffect。
总结
useLayoutEffect执行时机是 DOM 更新后、浏览器绘制前,同步阻塞,无视觉闪屏;useEffect是绘制后异步执行,可能有闪屏;- 核心用途是同步读取/修改 DOM 布局(如计算尺寸、调整样式),避免用户看到中间态;
- 慎用耗时操作,优先用
useEffect,仅在需要同步操作 DOM 布局时使用useLayoutEffect。