不常用的Hooks之useLayoutEffect
useLayoutEffect
的函数声明与useEffect
相同,但是它会在所有的DOM
变更之后同步调用effect
。可以使用它来读取DOM
布局并同步触发重新渲染。在浏览器执行绘制之前,useLayoutEffect
内部的更新计划将被同步刷新。尽可能使用标准的
useEffect
以避免阻塞视觉更新。
1. useEffect
-
默认情况下,都应该使用
useEffect
。useEffect
将componentDidMount、componentDidUpdate
合并为同一个api
。 -
useEffect
是异步的,所谓的异步就是利用requestIdleCallback
,在浏览器空闲时间执行传入的callback
。 -
大部分情况下,用哪一个都是一样的,如果副作用执行比较长,比如大量计算,如果是
useLayoutEffect
就会造成渲染阻塞。
2. useLayoutEffect
-
这个是用在处理
DOM
的时候,当你的useEffect
里面的操作需要处理DOM
,并且会改变页面的样式,就需要用到它,否则可能会出现闪屏的问题,useLayoutEffect
里面的callback
函数会在DOM
更新完成后立即执行,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制。 -
建议
-
useLayoutEffect
需要在同步执行时使用,但是还是尽量别用,避免阻塞渲染 -
在服务端SSR渲染的时候,最好放弃
useLayoutEffect
-
3. 例子
-
使用useEffect
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'));
-
当点击
div
的时候,页面会更新一串随机数。当你连续点击时,你会发现这串数字在发生抖动。原因在于,当你每次点击
div
,count
会更新为0
,之后useEffect
内又把count
改为一串随机数。所以页面会先渲染成0,然后再渲染成随机数,由于更新很快,所以出现了闪烁。
-
-
使用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'));
- 和使用
useEffect
相比,当你点击div
,count
更新为0
,此时页面并不会渲染,而是等待useLayoutEffect
内部状态修改后,才会去更新页面,所以页面不会闪烁。
- 和使用
4. 总结
-
useLayoutEffect
相比useEffect
,通过同步执行状态更新可解决一些特性场景下的页面闪烁问题。 -
useEffect
可以满足大部分的场景,而且useLayoutEffect
会阻塞渲染,因此谨慎使用。