react hooks--useLayoutEffect
概述
◼ useLayoutEffect看起来和useEffect非常的相似,事实上他们也只有一点区别而已:
useEffect会在渲染的内容更新到DOM上后执行,不会阻塞DOM的更新;
useLayoutEffect会在渲染的内容更新到DOM上之前执行,会阻塞DOM的更新;
官方更推荐使用useEffect而不是useLayoutEffect。
◼ 如果我们希望在某些操作发生之后再更新DOM,那么应该将这个操作放到useLayoutEffect。
useLayoutEffect 的同步执行
useLayoutEffect 这个 hook 在执行的时候,也是先调用 destroy()
,再执行 create()
。和 useEffect 不同的是前者在 mutation 阶段执行,后者在 layout 阶段执行。与 useEffect 不同的是,它不用数组去存储销毁和创建函数,而是直接操作 fiber.updateQueue。
卸载上一次的 effect,发生在 mutation 阶段:
执行本次的 effect 创建,发生在 layout 阶段:
useEffect和useLayoutEffect作为组件的副作用,本质上是一样的。共用一套结构来存储effect链表。整体流程上都是先在render阶段,生成effect,并将它们拼接成链表,存到fiber.updateQueue上,最终带到commit阶段被处理。他们彼此的区别只是最终的执行时机不同,一个异步一个同步,这使得useEffect不会阻塞渲染,而useLayoutEffect会阻塞渲染。
演示示例
import React, { memo, useEffect, useLayoutEffect, useState } from 'react'
const App = memo(() => {
const [count, setCount] = useState(100)
// useEffect(() => {
// console.log('useEffect执行了');
// // 这里的执行逻辑就是先赋值为0执行一遍,再设置值重新又执行一遍,就会出现闪烁的现象
// if(count === 0){
// setCount(Math.random() + 99)
// }
// })
useLayoutEffect(() => {
console.log('useLayoutEffect执行了');
// 这里就不会出现闪烁现象且执行一次
if(count === 0){
setCount(Math.random() + 99)
}
})
return (
<div>
<h4>count: {count}</h4>
<button onClick={e => setCount(0)}>设置为0</button>
</div>
)
})
export default App
总结
与 useEffect 的用法完全一样,作用也基本相同,唯一的不同在于执行时机,它会在所有的 DOM 变更之后同步调用 effect, 可以使用它来 useEffect 不会阻塞浏览器的绘制任务,它会在页面更新之后才执行。 而 useLayoutEffect 跟 componentDidMount 和 componentDidUpdate 的执行时机一样,会阻塞页面渲染,如果当中有耗时 任务的话,页面就会卡顿。大多数情况下 useEffect 比 class 的生命周期函数性能更好,我们应该优先使用它。 如果你正在将代码从 class 组件迁移到使用 Hook 的函数组件,则需要注意 useLayoutEffect与 componentDidMount、componentDidUpdate 的调用阶段是一样的。但是,我们推荐你一开始先用 useEffect,只有当它出问题的时候再尝试使用 useLayoutEffect。