在React中,useLayoutEffect 是一个与 useEffect 类似的 Hook,但它们在执行时机上有所不同。useLayoutEffect 的调用是在所有DOM变更之后,浏览器绘制之前同步执行的。这意味着你可以使用它来读取或修改DOM,而不会造成可见的闪烁。useEffect 则是在浏览器绘制之后异步执行的,通常用于数据获取、订阅或其他不需要立即反映在DOM上的操作。
从源码层面来看,useLayoutEffect 和 useEffect 都是通过调度更新来工作的。但是,useLayoutEffect 会在所有DOM变更之后立即同步调用,而 useEffect 会在浏览器完成绘制后延迟调用。这就是为什么 useLayoutEffect 更适合处理DOM测量和同步更新的原因。
以下是 useLayoutEffect 的一个简单示例,展示了如何在组件渲染后同步测量DOM节点的尺寸:
import { useLayoutEffect, useRef, useState } from 'react';
function MyComponent() {
const ref = useRef(null);
const [height, setHeight] = useState(0);
useLayoutEffect(() => {
setHeight(ref.current.clientHeight);
}, []);
return (
<div ref={ref}>
{/* ... */}
</div>
);
}
在这个示例中,useLayoutEffect 用于在组件渲染到屏幕后立即测量 div 元素的高度,并在状态中保存这个值。由于 useLayoutEffect 在浏览器绘制之前执行,用户不会看到任何闪烁或布局变化。
执行时机:
useLayoutEffect 在DOM更新完成后,浏览器绘制之前同步执行。这意味着可以在浏览器绘制前读取或修改DOM,避免出现闪烁。
useEffect 在DOM更新后,浏览器绘制之后的下一个宏任务中执行。通常用于不需要立即反映在DOM上的操作,如数据请求。
用途:
useLayoutEffect 适用于需要同步执行的操作,特别是那些对布局有影响的DOM操作,如测量布局大小。
useEffect 适用于不需要立即反映在DOM上的副作用,如API调用、设置订阅等。
服务端渲染(SSR):
在SSR中,useLayoutEffect 不会执行,因为它依赖于浏览器环境。如果在SSR中使用,可能会导致客户端和服务端内容不一致。
useEffect 在SSR中也不会执行,但由于它通常用于不影响DOM的操作,所以不会导致不一致问题。
总的来说,useLayoutEffect 应该在需要同步执行副作用操作,特别是那些涉及DOM测量和可能影响布局的操作时使用。如果副作用与DOM无关,或者不需要同步执行,那么 useEffect 是更好的选择。在服务端渲染(SSR)中,由于 useLayoutEffect 不会执行,可能会导致客户端和服务端渲染的内容不一致,因此在SSR中应避免使用 useLayoutEffect,或者使用条件判断来替换为 useEffect