useMemo与useEffect + useState

Ben*_*ams 17 javascript typescript reactjs react-hooks

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

这是两个自定义钩子,它们的乍看效果完全相同,除了useMemo的返回值null位于第一个渲染器上:

useEffect和useState
const useCalculate = numberProp => {
  const [result, setResult] = useState<number>(null);

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

  return result;
};
Run Code Online (Sandbox Code Playgroud) useMemo
const useCalculateWithMemo = numberProp => {
  return useMemo(() => {
    return expensiveCalculation(numberProp);
  }, [numberProp]);
};
Run Code Online (Sandbox Code Playgroud)

https://codesandbox.io/s/nkxolxwzkj

两者都计算每次“道具”更改时,useMemo踢球的“缓存” 在哪里?

现实世界中有哪些示例useMemo

sch*_*kin 29

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

  1. 函数调用的时间。

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

  1. 语义保证。

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

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

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

  • useEffect 不保证在依赖项未更改时不会重新渲染。useMemo 是在依赖项未更改时不会触发的一种。我从未在 useMemo 上遇到过额外重新渲染的问题,这与 useEffect 不同,useEffect 有时会在依赖项未更改时重新渲染。相反,您需要注意 useMemo 难以检测某些结构复杂的依赖项中的某些更改。(例如,数组内部对象的更改)。 (2认同)

win*_*mao 20

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

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

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

这意味着,useMemo是比较紧急的,然后useLayoutEffect是最后的useEffect


Ret*_*sam 17

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


假设我们有:

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

假设numberProp最初为0:

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

然后,如果我们更改numberProp为2:

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

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

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

因此,最好只useMemo在这里使用。

  • 我认为 useEffect 在一些长时间运行的同步场景中也很有用。查看下面的沙箱。加载需要 5 秒,因为 useMemo 在长计算运行时保留渲染线程,而 useEffect/useState 可以在计算运行时显示“旋转器”,因此不会阻止渲染:https:// codesandbox.io/s/usememo-vs-useeffect-usestate-ye6qm @Retsam (6认同)
  • 除了优化之外,我还使用 `useMemo` 而不是 `useState` + `useEffect` 模式,因为渲染越多,调试就越困难。 (6认同)
  • 我们是否可以由此得出结论,在要计算的内容是“异步”的情况下,“useEffect”+“useState”是正确的解决方案,因为无论如何该值在当前渲染中都不可用?相关[问题](/sf/ask/4322620991/)。 (3认同)
  • 值得注意的是,React API 文档提到“useMemo”并不能保证如果依赖项不改变,记忆函数不会再次执行,因为 React 将来可能会丢弃缓存以提高性能。因此,如果记忆函数有某种副作用,那么使用自定义钩子可能会更明智。 (2认同)
  • @Abhi 更改道具会触发重新渲染。但渲染的值基于“[result, setResult]”状态,并且“setResult”在“useEffect”运行之前不会被调用,这发生在渲染之后。 (2认同)
  • “就昂贵计算运行的频率而言,两者具有相同的行为” - 不一定。请参阅[此答案](/sf/answers/4699194811/)。“useEffect 保证如果依赖关系没有改变,它不会被触发。useMemo 不提供这样的保证。” 所以存在语义上的差异。即使依赖项没有改变, useMemo 也可以多次运行昂贵的计算。 (2认同)