【问题标题】:useMemo vs. useEffect + useStateuseMemo 与 useEffect + useState
【发布时间】:2019-09-25 11:44:24
【问题描述】:

使用useMemo(例如用于密集的函数调用)而不是使用useEffectuseState 的组合有什么好处?

除了useMemo 在第一次渲染时返回值为null 之外,这里有两个自定义钩子乍一看完全一样:

useEffect & useState

import { expensiveCalculation } from "foo";

function useCalculate(someNumber: number): number {
  const [result, setResult] = useState<number>(null);

  useEffect(() => {
    setResult(expensiveCalculation(someNumber));
  }, [someNumber]);

  return result;
}

使用备忘录

import { expensiveCalculation } from "foo";

function useCalculateWithMemo(someNumber: number): number {
    return useMemo(() => {
        return expensiveCalculation(someNumber);
    }, [someNumber]);
};

每次参数someNumber变化时都计算结果,useMemo的记忆在哪里踢?

【问题讨论】:

  • 第一次渲染时第一个将是null,而第二个不会?
  • 使用 useMemo 有什么好处(例如,用于密集的函数调用) - 是的。您正在使用专门为此目的设计的钩子。您列出的示例是 useMemo 在现实世界中最常见的示例。

标签: javascript reactjs typescript react-hooks


【解决方案1】:

useEffectsetState 将在每次更改时导致额外的渲染:第一次渲染将“滞后”旧数据,然后它会立即使用新数据排队进行额外的渲染。


假设我们有:

function expensiveCalculation(x) { return x + 1; }; // Maybe I'm running this on a literal potato

让我们假设 someNumber 最初是 0:

  • useMemo 版本会立即呈现 1
  • useEffect 版本渲染 null,然后在组件渲染后运行效果,更改状态,并使用 1 排队新的渲染。

那么如果我们把someNumber改为2:

  • useMemo 运行,3 被渲染。
  • useEffect 版本运行,并再次渲染1,然后效果触发,组件以正确的3 值重新运行。

expensiveCalculation 的运行频率而言,两者具有相同的行为,但useEffect 版本会导致两倍的渲染量,这出于其他原因不利于性能。

另外,IMO,useMemo 版本更加简洁易读。它不会引入不必要的可变状态,并且活动部件更少。

所以你最好在这里使用useMemo

【讨论】:

  • 我认为 useEffect 在一些长时间运行的同步场景中也很有用。查看下面的沙箱。加载需要 5 秒,因为 useMemo 在长时间计算运行时持有渲染线程,而 useEffect/useState 可以在计算运行时显示“微调器”,因此不会阻止渲染:codesandbox.io/s/usememo-vs-useeffect-usestate-ye6qm@雷萨姆
  • 除了优化之外,我使用 useMemo 而不是 useState + useEffect 模式,因为渲染越多,调试就越困难。
  • 值得注意的是,React API 文档提到 useMemo 不保证如果依赖关系没有改变,memoized 函数将不会再次执行,因为 React 将来可能会,丢弃缓存以提高性能。因此,如果 memoized 函数有某种副作用,使用自定义钩子可能会更聪明。
  • @Abhi 更改道具会触发重新渲染。但是渲染的值是基于[result, setResult] 状态的,在useEffect 运行之前不会调用setResult,这发生在渲染之后
  • 我们能否由此得出结论,useEffect + useState 是在要计算的东西是 async 的情况下的正确解决方案,因为无论如何该值在当前渲染中都不可用?相关question.
【解决方案2】:

我认为在它们之间进行选择时应该考虑两个要点。

  1. 函数调用的时间。

useEffect 在组件渲染后调用,因此您可以从中访问 DOM。例如,如果您想通过 refs 访问 DOM 元素,这很重要。

  1. 语义保证。

useEffect 保证如果依赖关系没有改变,它不会被触发。 useMemo 不提供此类保证。

正如React documentation 中所述,您应该将 useMemo 视为纯优化技术。即使您将 useMemo 替换为常规函数调用,您的程序也应该继续正常工作。

useEffect + useState 可用于控制更新。甚至打破循环依赖并防止无限更新循环。

【讨论】:

    【解决方案3】:

    我想说,除了异步性质之外,它们的设计方式可能会有所不同。

    useEffect 是集体调用,异步与否,在所有组件渲染完成后收集。

    useMemo 是本地调用,只和这个组件有关。您可以将useMemo 视为另一个赋值语句,它具有使用上次更新的赋值的好处。

    这意味着,useMemo 更紧急,然后是useLayoutEffect,最后是useEffect

    【讨论】:

      猜你喜欢
      • 2019-10-14
      • 2019-11-23
      • 1970-01-01
      • 2021-12-01
      • 2019-12-31
      • 1970-01-01
      • 2019-11-16
      • 2021-09-29
      • 2020-02-17
      相关资源
      最近更新 更多