前言
React hooks
已经使用了近一年的时间,useLayoutEffect
就像是个大神一样,静静的在那儿看着,始终找不到用武之地...
Layout
是布局、设计、陈列
的意思。难道是和操作DOM
相关?
useLayoutEffect的使用方法,官方文档解释如下:
useLayoutEffect
的函数签名与useEffect
相同,但是它会在所有的DOM
变更之后同步调用effect
。可以使用它来读取DOM
布局并同步触发重新渲染。在浏览器执行绘制之前,useLayoutEffect
内部的更新计划将被同步刷新。
尽可能使用标准的 useEffect
以避免阻塞视觉更新。
提示:
如果你正在将代码从 class
组件迁移到使用 Hook
的函数组件,则需要注意 useLayoutEffect
与 componentDidMount
、componentDidUpdate
的调用阶段是一样的。但是,我们推荐你一开始先用 useEffect
,只有当它出问题的时候再尝试使用 useLayoutEffect
。
如果你使用服务端渲染,请记住,无论useLayoutEffect
还是 useEffect
都无法在 Javascript
代码加载完成之前执行。这就是为什么在服务端渲染组件中引入 useLayoutEffect
代码时会触发 React
告警。解决这个问题,需要将代码逻辑移至 useEffect
中(如果首次渲染不需要这段逻辑的情况下),或是将该组件延迟到客户端渲染完成后再显示(如果直到 useLayoutEffect
执行之前 HTML
都显示错乱的情况下)。
若要从服务端渲染的 HTML
中排除依赖布局 effect
的组件,可以通过使用 showChild && <Child />
进行条件渲染,并使用 useEffect(() => { setShowChild(true); }, [])
延迟展示组件。这样,在客户端渲染完成之前,UI
就不会像之前那样显示错乱了。
看完官方解释表示还是一脸懵,连个Demo都不给,只能总结出如下两点:
useLayoutEffect
需要在同步执行时使用,但是还是尽量别用,避免阻塞渲染- 在服务端SSR渲染的时候,最好放弃
useLayoutEffect
useEffect
默认情况下,都应该使用useEffect
。useEffect
将componentDidMount、componentDidUpdate
合并为同一个api
。
useEffect
是异步的,所谓的异步就是利用requestIdleCallback
,在浏览器空闲时间执行传入的callback
。大部分情况下,用哪一个都是一样的,如果副作用执行比较长,比如大量计算,如果是useLayoutEffect
就会造成渲染阻塞。
useLayoutEffect
这个是用在处理DOM
的时候,当你的useEffect
里面的操作需要处理DOM
,并且会改变页面的样式,就需要用到它,否则可能会出现闪屏的问题,useLayoutEffect
里面的callback
函数会在DOM
更新完成后立即执行,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制。
下面是大家都用的一个比较经典的例子:
import React, { useEffect, useState, useLayoutEffect, useRef } from 'react';
import { render } from 'react-dom';
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
if (count === 0) {
const randomNum = 10 + Math.random()*200
setCount(10 + Math.random()*200);
}
}, [count]);
return (
<div onClick={() => setCount(0)}>{count}</div>
);
}
render(<App />, document.getElementById('root'));
可以在线进行试验 stackblitz.com/edit/react-…
当点击div
的时候,页面会更新一串随机数。当你连续点击时,你会发现这串数字在发生抖动。
原因在于,当你每次点击div
,count
会更新为0
,之后useEffect
内又把count
改为一串随机数。
所以页面会先渲染成0,然后再渲染成随机数,由于更新很快,所以出现了闪烁。
接下来我们将 useEffect 改为 useLayoutEffect:
import React, { useEffect, useState, useLayoutEffect, useRef } from 'react';
import { render } from 'react-dom';
function App() {
const [count, setCount] = useState(0);
useLayoutEffect(() => {
if (count === 0) {
const randomNum = 10 + Math.random()*200
setCount(10 + Math.random()*200);
}
}, [count]);
return (
<div onClick={() => setCount(0)}>{count}</div>
);
}
render(<App />, document.getElementById('root'));
可以在线进行试验 stackblitz.com/edit/react-…
相比使用 useEffect
,当你点击 div
,count
更新为 0
,此时页面并不会渲染,而是等待 useLayoutEffect
内部状态修改后,才会去更新页面,所以页面不会闪烁。
总结:
useLayoutEffect
相比useEffect
,通过同步执行状态更新可解决一些特性场景下的页面闪烁问题。useEffect
可以满足大部分的场景,而且useLayoutEffect
会阻塞渲染,因此谨慎使用。
参考文档: uselayouteffect