React.js学习-useEffect vs useLayoutEffect
大部分情况下不需要明确区分 useEffect
和 useLayoutEffect
(至少我还没遇到过 😂,不过有必要了解一下,没准哪天就遇到了)
两者唯一的区别在于执行时机的不同
useEffect
一般看来,对于 useEffect
,为了防止浏览器渲染卡顿,其回调函数会在浏览器 paint
之后执行,这看起来很正常,下面是一个正常的组件渲染流程:
做一些准备工作(渲染
VDOM
、调度effects
等)React 将控制权交由浏览器以
paint
执行
useEffect
回调
确实,大部分情况下是这样,只不过凡事都有例外,且看官网的描述:
虽然
useEffect
会在浏览器绘制后延迟执行,但会保证在任何新的渲染前执行。在开始新的更新前,React 总会先清除上一轮渲染的effect
。
官网的介绍会保证在任何新的渲染前执行,并没有说一定会在浏览器 paint
之后执行,因此是不是有一些场景 effect
会在 paint
之前执行?🤔
如果在 useEffect
的回调触发之前,React 组件又进行了一次状态更新,React 会先将之前的 Passive Effect 都处理掉。例如在 useLayoutEffect
中更新状态,在这种情况下,useEffect
的回调 会在 paint 之前执行
useLayoutEffect
和 useEffect
的唯一区别在于执行时机不同, useLayoutEffect
一定会保证回调函数会在 paint 之前执行,下面是一个正常的组件渲染流程:
做一些准备工作(渲染
VDOM
、调度effects
等)执行
useLayoutEffect
回调React 将控制权交由浏览器以
paint
执行
useEffect
回调
这个区别的体现场景在于使用 useEffect 更新数据时,界面会闪烁一下
const { useState, useEffect, useLayoutEffect } = React;
const App = () => {
const [value, setValue] = useState(-1);
useEffect(() => {
let i = 0;
while (i <= 900000000) {
i++;
}
if (value === 0) {
const tmp = Math.random() * 100;
console.log("setValue", tmp);
setValue(() => tmp);
}
}, [value]);
console.log("render", value, Date.now());
return <div onClick={() => setValue(0)}>{value}</div>;
};
可以看到有一个明显的闪烁
改用 useLayoutEffect
后就不会出现这种情况了