这是我参与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