image.png

原文地址:https://daveceddia.com/useeffect-vs-uselayouteffect/

有两个 React 钩子 useEffectuseLayoutEffect,它们看起来几乎一样。

它们的调用方式甚至都相同。

  1. useEffect(() => {
  2. // do side effects
  3. return () => {} /* cleanup */
  4. }, [dependency, array]);
  5. useLayoutEffect(() => {
  6. // do side effects
  7. return () => {} /* cleanup */
  8. }, [dependency, array]);

但是它们并不完全相同,请继续往下读,了解它们不同的原因以及何时使用它们。
(长话短说:大多数时候请使用普通的 useEffect

useEffect 和 useLayoutEffect 之间的区别

一切都在时机上。

useEffec 异步运行,并且在将渲染绘制到屏幕上之后。

所以看起来像:

  1. 您以某种方式触发渲染(更改状态或父级重新渲染)
  2. React 渲染您的组件
  3. 屏幕以视觉方式更新
  4. 然后 useEffect 运行

useLayoutEffect 在渲染之后但屏幕更新之前同步运行。步骤如下:

  1. 您以某种方式触发渲染(更改状态或父级重新渲染)
  2. React 渲染您的组件
  3. useLayoutEffect 运行,React 会等待它执行完成(阻塞 UI)
  4. 屏幕以视觉方式更新

99% 的情况使用 useEffect

在大多数情况下,effect 函数会同步一些 state 或 props,它并不需要立即执行,或者直接影响页面。

就像您要拉取数据一样,这不会导致立即变更页面。

或者,如果您要设置事件处理程序。

或者,如果您要在对话框出现或消失时重置某些状态。

大多数时候,使用 useEffect 才是正确的方式。

何时使用 LayoutEffect

什么时候使用 useLayoutEffect 代替 useEffect 呢?看到它的名字你就能理解到了 :)

如果您的组件在状态更新时闪烁(例如:它首先展示为部分初始状态,然后立即重新渲染为最终状态),则可以使用 useLayoutEffect

这是一个(人为的)演示,借助此例子您可以理解我想表达的意思。

当您单击页面* 时,状态立即更改(value重置为 0),这将重新渲染该组件:效果是将 value 设置为某个随机数,然后再次重新渲染。

  1. import React, {
  2. useState,
  3. useLayoutEffect
  4. } from 'react';
  5. import ReactDOM from 'react-dom';
  6. const BlinkyRender = () => {
  7. const [value, setValue] = useState(0);
  8. useLayoutEffect(() => {
  9. if (value === 0) {
  10. setValue(10 + Math.random() * 200);
  11. }
  12. }, [value]);
  13. console.log('render', value);
  14. return (
  15. <div onClick={() => setValue(0)}>
  16. value: {value}
  17. </div>
  18. );
  19. };
  20. ReactDOM.render(
  21. <BlinkyRender />,
  22. document.querySelector('#root')
  23. );

* 顺便提一下:在 div 上绑定 onClick 事件不利于可访问性,这里只是演示。

尝试在 CodeSandbox 上在线运行 Demo:

useLayoutEffect.gif

  • 使用 useEffect (50 帧每秒)

useEffect.gif

请注意,即使组件渲染了两次,该版本的 useLayoutEffect 外观也只有视觉更新一次。 useEffect 另一方面,该版本在视觉上呈现两次,因此您会看到闪烁,其中的值是短暂的 0

我应该使用 useEffect 还是 useLayoutEffect?

大多数时候, useEffect 是正确的选择。如果您的代码导致闪烁,请切换至 useLayoutEffect 并查看是否有帮助。

因为 useLayoutEffect 是同步(也称为阻塞),所以该应用在效果完成运行之前不会进行视觉更新…… 如果效果中的代码很慢,则可能会导致性能问题。再加上大多数效果在运行时不需要世界暂停。