引言
在React中,useEffect 和 useLayoutEffect 是两个非常关键的Hook,用于处理组件的副作用(side effects)。尽管它们在很多方面相似,但它们在执行时机和执行方式上却有着本质的区别。
useLayoutEffect 的基础
使用方式
useLayoutEffect 接收一个函数和一个依赖项数组作为参数。这个函数会在组件的渲染阶段同步执行,即在浏览器绘制屏幕之前执行,从而有机会在DOM更新之前对DOM进行操作或读取最新的DOM状态。如果依赖项数组中的任何值发生变化,useLayoutEffect中的函数会重新执行。
返回值
和useEffect一样,useLayoutEffect也可以返回一个清理函数,该函数会在组件卸载或依赖项变化导致的重新渲染之前执行,用于清理副作用。
useLayoutEffect 与 useEffect 的区别
执行时机
- useEffect:在浏览器完成DOM更新后异步执行,不会阻塞浏览器绘制过程。
- useLayoutEffect:在浏览器绘制DOM之前同步执行,会阻塞浏览器的绘制过程。
场景应用
- useEffect:适用于大多数副作用操作,如数据获取、订阅或手动更改DOM(这些操作不需要在DOM更新前完成)。
- useLayoutEffect:适用于需要在DOM更新前同步读取DOM尺寸或进行DOM操作的场景,如调整滚动位置、焦点管理等。
实际案例
场景:避免DOM闪烁
假设我们有一个组件,它显示一个数字,并允许用户点击按钮将数字重置为0。然而,如果重置后的逻辑处理(如将数字改为一个随机数)在DOM更新后执行,用户可能会看到数字从0瞬间变为另一个随机数,导致界面闪烁。
import React, { useState, useEffect, useLayoutEffect } from 'react';
function RandomNumber() {
const [num, setNum] = useState(Math.random() * 200);
// 使用 useEffect 时,界面会闪烁
// useEffect(() => {
// if (num === 0) {
// setNum(10 + Math.random() * 200);
// }
// }, [num]);
// 使用 useLayoutEffect 避免闪烁
useLayoutEffect(() => {
if (num === 0) {
setNum(10 + Math.random() * 200);
}
}, [num]);
return (
<>
<h1>num 的值是: {num}</h1>
<button onClick={() => setNum(0)}>重置num</button>
</>
);
}
export default RandomNumber;