useLayoutEffect
使用
useLayoutEffect函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用effect。
可以使用它来读取 DOM 布局并同步触发重渲染。
在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
尽可能使用标准的 useEffect 以避免阻塞视觉更新。
import React, { useState, useEffect, useLayoutEffect } from "react";
export default function Demo() {
let [num, setNum] = useState(0);
// 再试试useLayoutEffect
useEffect(() => {
if (num === 0) {
let random = +String(Math.random()).substring(2);
setNum(random);
}
}, [num]);
return <div
style={{
background: 'lightblue',
WebkitUserSelect: "none"
}}
onClick={() => {
setNum(0);
}}>
{num}
</div>;
};
useEffect和useLayoutEffect区别
useLayoutEffect会阻塞浏览器渲染真实DOM【真实DOM对象已经创建了】,优先执行Effect链表中的callback;
useEffect不会阻塞浏览器渲染真实DOM,在渲染真实DOM的同时,去执行Effect链表中的callback
- 它们里面的回调函数都是放在effect链表中的,但是useLayoutEffect设置的callback要优先于useEffect去执行
- 在两者设置的callback中,依然可以获取DOM元素「原因:真实DOM对象已经创建了,区别只是浏览器是否渲染」
- 如果在callback函数中又修改了状态值「视图又要更新」
- useEffect:浏览器肯定是把第一次的真实已经绘制了,再去渲染第二次真实DOM【频繁切换有闪烁】
- useLayoutEffect:浏览器是把两次真实DOM的渲染,合并在一起渲染的【频繁切换无闪烁】
视图更新周期:
第一步:基于babel-preset-react-app把JSX编译为createElement格式
第二步:执行createElement(…)方法,创建出virtualDOM
第三步:基于root.render方法把virtualDOM变为真实DOM对象「DOM-DIFF」
-
useLayoutEffect阻塞浏览器绘制:在整个视图渲染更新周期中,创建出真实DOM以后直接执行useLayoutEffect的effect链表中的方法。如果该方法有setXXX操作,那么会直接进入下一次更新周期中,而不会执行第四步。因此无论视图更新的过程执行了多少次,界面永远只看到了一次变化,即【频繁切换无闪烁】
-
useEffect不阻塞浏览器绘制:在React渲染完真实DOM之后、浏览器绘制完毕之前会执行effect链表中的方法,同时第四步也会异步执行。如果effect链表中的方法有setXXX操作,那么会直接进入下一次更新周期中,同时上一次更新周期中的第四步还在执行,浏览器还在重绘。如果在useEffect中频繁触发更新,后台会异步运行多个“第四步”,由于浏览器绘制是需要一定时间的,因此对于速度较慢的设备,用户会看到多次重绘之间的“白屏”,即【频繁切换有闪烁】
第四步:浏览器渲染和绘制真实DOM对象
从视图更新周期可以看出,useLayoutEffect和useEffect都是可以获取真实DOM的时机
官方文档示例参考
https://react.docschina.org/reference/react/useLayoutEffect#usage