React useLayoutEffect 钩子详解
useLayoutEffect
是 React 中的一个钩子函数,它与更常用的 useEffect
非常相似,但有一个关键区别:执行时机不同。
useLayoutEffect 的工作原理
useLayoutEffect
的执行过程如下:
- React 渲染组件,计算 DOM 更新
- DOM 变更被应用到屏幕(但用户还看不到)
- useLayoutEffect 同步执行
- 浏览器绘制更新后的 DOM(用户看到内容)
而 useEffect
的执行过程是:
- React 渲染组件,计算 DOM 更新
- DOM 变更被应用到屏幕
- 浏览器绘制更新后的 DOM(用户看到内容)
- useEffect 异步执行
使用场景
useLayoutEffect
主要适用于以下场景:
- 防止视觉闪烁:当你需要在用户看到页面之前进行 DOM 测量和修改时
- 同步 DOM 操作:需要在浏览器绘制之前同步更新 DOM
- 依赖于 DOM 测量的动画:需要测量元素尺寸并立即应用动画效果
代码示例
基本用法
import React, { useLayoutEffect, useRef } from 'react';
function MeasureExample() {
const divRef = useRef();
const [dimensions, setDimensions] = React.useState({ width: 0, height: 0 });
useLayoutEffect(() => {
// 这里的测量和更新会在浏览器绘制前完成,避免闪烁
const { width, height } = divRef.current.getBoundingClientRect();
setDimensions({ width, height });
}, []);
return (
<div>
<div ref={divRef}>测量我的尺寸</div>
<p>宽度: {dimensions.width}px, 高度: {dimensions.height}px</p>
</div>
);
}
防止闪烁的例子
import React, { useState, useLayoutEffect } from 'react';
function PreventFlickerExample() {
const [position, setPosition] = useState('left');
useLayoutEffect(() => {
// 如果使用普通的useEffect,会先看到元素在左边,然后跳到右边
// 使用useLayoutEffect可以确保用户只看到最终位置
if (position === 'left') {
setPosition('right');
}
}, [position]);
return (
<div style={{
position: 'relative',
height: '100px',
width: '100%'
}}>
<div style={{
position: 'absolute',
left: position === 'left' ? '0px' : '300px',
transition: 'left 0.5s'
}}>
移动的方块
</div>
</div>
);
}
useLayoutEffect vs useEffect
特性 | useLayoutEffect | useEffect |
---|---|---|
执行时机 | DOM 更新后,浏览器绘制前(同步) | 浏览器绘制后(异步) |
阻塞渲染 | 是 | 否 |
适用场景 | DOM 测量和修改,防止闪烁 | 数据获取,订阅,手动DOM修改 |
性能影响 | 可能延迟视觉更新 | 对渲染性能影响小 |
最佳实践
- 默认使用 useEffect:除非有特殊需求,否则优先使用
useEffect
- 仅在必要时使用 useLayoutEffect:当需要在用户看到页面之前进行DOM操作时
- 保持回调函数简洁:由于会阻塞渲染,应避免在
useLayoutEffect
中执行耗时操作