useLayoutEffect 是 React 提供的一个 Hook,它的签名和 useEffect 完全相同,但它们的执行时机不同。
基本用法
javascript
import { useLayoutEffect } from 'react';
useLayoutEffect(() => {
// 执行一些 DOM 操作或同步逻辑
return () => {
// 清理逻辑(可选)
};
}, [dependencies]);
与 useEffect 的区别
| 特性 | useEffect |
useLayoutEffect |
|---|---|---|
| 执行时机 | 在浏览器完成绘制(paint)之后异步执行 | 在浏览器绘制之前同步执行(在 DOM 更新后、屏幕更新前) |
| 是否阻塞渲染 | 否(不会阻塞页面渲染) | 是(会阻塞页面渲染) |
| 适用场景 | 数据获取、订阅、日志记录等副作用 | 需要同步读取/修改 DOM、避免视觉闪烁的场景 |
为什么需要 useLayoutEffect?
有时你需要在 DOM 更新后立即 读取布局信息(如元素尺寸、位置),并基于这些信息进行进一步的 DOM 修改。如果使用 useEffect,由于它是异步执行的,用户可能会先看到一个"中间状态"(比如元素跳动或闪烁),而 useLayoutEffect 可以在浏览器真正绘制前完成所有操作,从而避免这种视觉问题。
示例:避免视觉闪烁
javascript
function MeasureExample() {
const [width, setWidth] = useState(0);
const ref = useRef();
useLayoutEffect(() => {
// 立即测量并设置宽度,防止闪烁
if (ref.current) {
setWidth(ref.current.offsetWidth);
}
}, []);
return (
<div>
<div ref={ref}>Some content</div>
<p>Width: {width}px</p>
</div>
);
}
如果这里用 useEffect,页面可能会先渲染 Width: 0px,然后很快变成实际宽度,造成一次视觉跳动;而 useLayoutEffect 能确保在首次绘制时就显示正确的宽度。
注意事项
- 慎用 :因为
useLayoutEffect是同步执行的,会阻塞浏览器绘制,过度使用会影响性能。 - 服务端渲染(SSR) :
useLayoutEffect在服务端不会执行(和useEffect一样),但 React 会在客户端 hydration 时发出警告(因为服务端没有 layout 阶段)。如果遇到警告,可以考虑延迟渲染或改用useEffect。
总结
- 默认优先使用
useEffect。 - 只有当你需要同步读取并修改 DOM ,且必须在浏览器绘制前完成时,才使用
useLayoutEffect。