
3.2.3 useLayoutEffect
React还提供了与useEffect同等地位的useLayoutEffect。useEffect和useLayoutEffect在副作用中都可获得DOM变更后的属性:

单击按钮后会打印“color layout effect red”“color effect red”。可见useEffect与useLayoutEffect都可从DOM中获得其变更后的属性。
从表面上看,useEffect与useLayoutEffect并无区别,但事实上厘清它们的区别需要从副作用的“同步”“异步”入手。3.2.2节曾介绍过useEffect的运行过程是异步进行的,即useEffect不阻碍浏览器的渲染;useLayoutEffect与useEffect的区别是useLayoutEffect的运行过程是“同步”的,其阻碍浏览器的渲染。
简而言之,useEffect发生在浏览器reflow/repaint操作之后,如果某些effect是从DOM中获得值的,如获取clientHeight、clientWidth,并需要对DOM进行变更,则可以改用useLayoutEffect,使得这些操作在reflow/repaint操作之前完成,这样有机会避免浏览器花费大量成本,多次进行reflow/repaint操作。以一个例子来说明:

分别在useEffect、requestAnimationFrame、useLayoutEffect和render过程中进行调试打印,以观察它们的时序。可以看到,当渲染App后将按如下顺序打印:render、layoutEffect、requestAnimationFrame、effect。由此可知,useLayoutEffect的副作用都在“绘制”阶段前,useEffect的副作用都在“绘制”阶段后。通过浏览器调试工具观察task的执行,如图3-2所示。
在图3-2中,①执行了useLayoutEffectCallback,为useLayoutEffect的副作用;②为浏览器的Paint流程;在Paint流程后,③的执行函数为useEffectCallBack,执行了useEffect的副作用。

图3-2 useLayoutEffectCallback与useEffectCallBack的执行顺序