useMemo 与 useEffect + useState

IT技术 javascript reactjs typescript react-hooks
2021-04-17 11:03:47

是否有使用任何好处useMemo(例如,对于密集的函数调用),而不是使用的组合useEffectuseState

这里有两个自定义钩子,除了useMemonull一次渲染时的返回值之外,乍一看完全相同

在 CodeSandbox 上查看

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变化时都计算结果,kick useMemoin的记忆在哪里?

3个回答

useEffectsetState每个变化都会导致额外的呈现:第一个将呈现“落后”与陈旧的数据,然后它会立即排队,用新的数据的附加渲染。


假设我们有:

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

让我们假设someNumber最初是 0:

  • useMemo版本立即呈现1.
  • useEffect版本呈现null,组件呈现然后后的效果运行,改变了状态,和队列了一个新的与渲染1

那么如果我们改成someNumber2:

  • useMemo运行和3呈现。
  • useEffect版本运行,并呈现1再次,那么效果触发,并与正确值的组件重播3

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

另外useMemo,IMO ,该版本更清晰、更具可读性。它不会引入不必要的可变状态并且具有更少的移动部件。

所以你最好useMemo在这里使用

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

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

  1. 函数调用的时间。

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

  1. 语义保证。

useEffect保证在依赖项没有改变的情况下它不会被触发。useMemo不提供此类保证。

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

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

我想说除了异步性质之外,它们的设计方式可能存在一些差异。

useEffect 是一个集体调用,无论异步与否,它都是在所有组件都呈现后收集的。

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

这意味着,useMemo更紧迫,然后useLayoutEffect和最后一个useEffect