useMemo 与 useEffect + useState
Posted
技术标签:
【中文标题】useMemo 与 useEffect + useState【英文标题】:useMemo vs. useEffect + useState 【发布时间】:2019-09-25 11:44:24 【问题描述】:使用useMemo
(例如用于密集的函数调用)而不是使用useEffect
和useState
的组合有什么好处?
除了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】:
useEffect
和 setState
将在每次更改时导致额外的渲染:第一次渲染将“滞后”旧数据,然后它会立即使用新数据排队进行额外的渲染。
假设我们有:
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】:
我认为在它们之间进行选择时应该考虑两个要点。
-
函数调用的时间。
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