这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战
useEffect
默认情况下,useEffect将在每轮渲染结束后异步执行,不同于class Component中的componentDidUpdate和componentDidMount在渲染后同步执行,useEffect不会阻塞浏览器的渲染。
useLayoutEffect
useLayoutEffect的作用几乎与useEffect一致,不同的是,useLayoutEffect是同步执行的,与componentDidUpdate和componentDidMount执行机制一样,在DOM更新后,在浏览器渲染这些更改之前,立即执行。
使用场景
项目中几乎99%的情况下,使用useEffect.
场景一
- 如果组件在状态更新时会闪一下:即开始显示初始状态的值,然后显示更新后的值,中间变换时间间隔非常短,看起来的交互就会像是闪一下,如果要避免这情况的话,可以把
useEffect替换为useLayoutEffect。 - 这正是因为,
useLayoutEffect是在本次更新准备完成之前,同步执行它的回调函数(它会阻塞页面渲染),而useLayoutEffect内部还有触发组件更新的操作,所以初始状态的虚拟DOM的值会被二次重新更新,完成之后才会更新至真实DOM,最终表现出只更新一个DOM的效果,减少了真实 DOM 的回流或重绘的消耗,所以不会有闪烁的情况。如下代码:
import React, {
useState,
useLayoutEffect
} from 'react';
import ReactDOM from 'react-dom';
const BlinkyRender = () => {
const [value, setValue] = useState(0);
useLayoutEffect(() => {
if (value === 0) {
setValue(10 + Math.random() * 200);
}
}, [value]);
return (
<div onClick={() => setValue(0)}>
value: {value}
</div>
);
};
ReactDOM.render(
<BlinkyRender />,
document.querySelector('#root')
);
场景二
在任何其它可能会更新一个变量(比如ref)的代码运行前,如果你想访问这个变量此时最新的值,可以用useLayoutEffect,以下面代码为例:
const ref = React.useRef()
React.useEffect(() => {
ref.current = 'new value' // 影响 ref更新的代码
})
React.useLayoutEffect(() => {
console.log(ref.current) // 此时获取的为更新前的值,而不是 "new value"
})
总结
- 大部分
99%情况下使用异步执行非阻塞的useEffect,用户更快的可以看到DOM更新 - 非特殊情况下一般不建议使用
useLayoutEffect useLayoutEffect的执行时机是在DOM更新后,浏览器完成渲染(绘制)之前执行
参考
useEffect vs useLayoutEffect
When to useLayoutEffect Instead of useEffect