useLayoutEffect()

74 阅读2分钟

useLayoutEffect与useEffect名字很像,用法也大致类似,但两者执行时机不同

useEffect 的函数会在组件渲染到屏幕之后执行
useLayoutEffect则是在DOM结构更新后、渲染前执行,相当于有一个防抖效果

根据上述特性,可以预计到如果在useEffect中瞬时连续更新某个状态,那么页面会发生多次渲染,现在举例测试。

  • useEffect()
import { useState, useEffect, useLayoutEffect } from "react";
import * as ReactDOM from "react-dom";

function 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>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

这个测试的意思是,在点击div的时候将value设为0,但在useEffect中又将其设为一个随机值。这样相当于value这个状态快速连续的变更了两次。

这个例子中组件的变化顺序是:

  1. click setState (value)
  2. 虚拟 DOM 设置到真实 DOM 上
  3. 渲染
  4. 执行useEffect回调
  5. setState(value)
  6. 虚拟 DOM 设置到真实 DOM 上
  7. 渲染

一共执行了两次渲染:3和7

  • useLayoutEffect()
import { useState, useEffect, useLayoutEffect } from "react";
import * as ReactDOM from "react-dom";

function App() {
  const [value, setValue] = useState(0);
  useLayoutEffect(() => {
    if (value === 0) {
      setValue(10 + Math.random() * 200);
    }
  }, [value]);
  console.log("render", value);
  return (
    <div onClick={() => setValue(0)}>value: {value}</div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

这里的打印结果和上面一样,但页面表现却不一样,页面并没有出现闪动,而是正常渲染。

这个例子中组件的变化顺序是:

  1. click setState (value)
  2. 虚拟 DOM 设置到真实 DOM 上
  3. 执行useLayEffect回调
  4. setState (value)
  5. 虚拟 DOM 设置到真实 DOM 上
  6. 渲染

只会进行一次页面渲染

但官网提到的一些应该注意的点:

  1. 应先使用useEffect,若出问题再尝试useLayoutEffect;
  2. useLayoutEffect与componentDidMount,componentDidUpdate的调用阶段是一样的;
  3. 针对服务端渲染:useEffect和useLayoutEffect都无法在JS代码加载完成之前执行。(笔者暂未接触服务端渲染,不清楚这意味着什么)