原文地址:https://pengfeixc.com/blog/605af93600f1525af762a725
首先要说明的是,useLayoutEffect和useEffect很像,函数签名也是一样。唯一的不同点就是useEffect是异步执行,而useLayoutEffect是同步执行的。
当函数组件刷新(渲染)时,包含useEffect的组件整个运行过程如下:
触发组件重新渲染(通过改变组件state或者组件的父组件重新渲染,导致子节点渲染)。
组件函数执行。
组件渲染后呈现到屏幕上。
useEffect
hook执行。
当函数组件刷新(渲染)时,包含useLayoutEffect的组件整个运行过程如下:
触发组件重新渲染(通过改变组件state或者组件的父组件重新渲染,导致子组件渲染)。
组件函数执行。
useLayoutEffect
hook执行, React等待useLayoutEffect的函数执行完毕。
组件渲染后呈现到屏幕上。
useEffect异步执行的优点是,react渲染组件不必等待useEffect函数执行完毕,造成阻塞。
百分之99的情况,使用useEffect就可以了,唯一需要用到useLayoutEffect的情况就是,在使用useEffect的情况下,我们的屏幕会出现闪烁的情况(组件在很短的时间内渲染了两次)。
例如,下面的代码,组件就会渲染两次。
import ReactDom from "react-dom"; import React, {useEffect, useLayoutEffect, useState} from "react"; const App = () => { const [value, setValue] = useState(0); useEffect(() => { if (value === 0) { setValue(10 + Math.random() * 200); } }, [value]); console.log("render", value); return ( <div onClick={() => setValue(0)}> value: {value} </div> ); }; ReactDom.render( <App />, document.getElementById("root") );
上面的代码,如果使用useEffect,当你点击div时,会出现短暂的闪烁,屏幕上会先出现0,然后出现useEffect中设定的数字,但是使用useLayoutEffect则不会出现闪烁的情况。
了解了useEffect的执行过程,我们可以通过useRef获得组件上一个状态的state和props,请看下面的例子:
function Counter() { const [count, setCount] = useState(0); const prevCountRef = useRef<number>(1); // 组件前一个状态 // useEffect会在组件函数执行后,在执行 useEffect(() => { prevCountRef.current = count; }); // 这一句会在useEffect函数执行前执行 const prevCount = prevCountRef.current; return ( <div> <h1>Now: {count}, before: {prevCount}</h1> <button onClick={() => setCount(count => count + 1)}>click</button> </div> ); }
因为useEffect会在组件函数执行之后执行,所以preCountRef会存储之前的值。
(完)