useMemo 与 useEffect + useState

Posted

技术标签:

【中文标题】useMemo 与 useEffect + useState【英文标题】:useMemo vs. 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 在现实世界中最常见的示例。 【参考方案1】:

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


假设我们有:

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

让我们假设 someNumber 最初是 0:

useMemo 版本会立即呈现 1useEffect 版本渲染 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】:

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

    函数调用的时间。

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

    语义保证。

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

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

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

【讨论】:

【参考方案3】:

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

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

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

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

【讨论】:

以上是关于useMemo 与 useEffect + useState的主要内容,如果未能解决你的问题,请参考以下文章

useEffect, useCallback, useMemo三者有何区别?

react hooks 中的 useEffect, useCallback, useMemo

react hooks 中的 useEffect, useCallback, useMemo

react hooks 中的 useEffect, useCallback, useMemo

React使用hooks-useMemo

React Hooks-useMemo篇