useMemo 与 useEffect + useState

useMemo vs. useEffect + useState

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

除了 useMemo 的 return 值在第一次渲染时为 null 之外,这里有两个乍一看完全相同的自定义挂钩:

使用效果和使用状态

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 的记忆在哪里开始?

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


假设我们有:

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

让我们假设 x 最初是 0:

  • useMemo 版本立即呈现 1
  • useEffect版本渲染null,然后组件渲染后效果运行,改变状态,排队新渲染1

那么如果我们把x改成2:

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

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

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

所以你最好在这里使用 useMemo

我认为你在选择它们时应该考虑两个要点。

  1. 调用函数的时间。

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

访问 DOM 元素,这一点很重要
  1. 语义保证。

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

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

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

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

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

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

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