useMemo 与 useEffect + useState
useMemo vs. useEffect + useState
使用 useMemo
(例如,对于密集函数调用)而不是组合使用 useEffect
和 useState
有什么好处吗?
除了 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
的记忆在哪里开始?
useEffect
和 setState
将在每次更改时导致额外的渲染:第一个渲染将“落后于”陈旧数据,然后它会立即排队使用新数据进行额外渲染数据。
假设我们有:
// 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
。
我认为你在选择它们时应该考虑两个要点。
- 调用函数的时间。
useEffect
在组件渲染后调用,因此您可以从中访问 DOM。例如,如果您想通过 refs.
访问 DOM 元素,这一点很重要
- 语义保证。
useEffect
保证如果依赖关系没有改变,它不会被触发。 useMemo
不提供此类保证。
如 React documentation 中所述,您应该将 useMemo 视为纯粹的优化技术。即使您将 useMemo 替换为常规函数调用,您的程序也应该继续正常工作。
useEffect
+ useState
可以用来控制更新。甚至打破循环依赖并防止无限更新循环。
我想说除了异步性质之外,它们的设计方式可能有所不同。
useEffect
是一个集体调用,异步与否,所有组件渲染完成后收集。
useMemo
是本地调用,只和这个组件有关系。您可以将 useMemo
视为另一个赋值语句,它有利于使用上次更新的赋值。
这意味着,useMemo
更紧急,然后是useLayoutEffect
,最后是useEffect
。
使用 useMemo
(例如,对于密集函数调用)而不是组合使用 useEffect
和 useState
有什么好处吗?
除了 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
的记忆在哪里开始?
useEffect
和 setState
将在每次更改时导致额外的渲染:第一个渲染将“落后于”陈旧数据,然后它会立即排队使用新数据进行额外渲染数据。
假设我们有:
// 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
。
我认为你在选择它们时应该考虑两个要点。
- 调用函数的时间。
useEffect
在组件渲染后调用,因此您可以从中访问 DOM。例如,如果您想通过 refs.
- 语义保证。
useEffect
保证如果依赖关系没有改变,它不会被触发。 useMemo
不提供此类保证。
如 React documentation 中所述,您应该将 useMemo 视为纯粹的优化技术。即使您将 useMemo 替换为常规函数调用,您的程序也应该继续正常工作。
useEffect
+ useState
可以用来控制更新。甚至打破循环依赖并防止无限更新循环。
我想说除了异步性质之外,它们的设计方式可能有所不同。
useEffect
是一个集体调用,异步与否,所有组件渲染完成后收集。
useMemo
是本地调用,只和这个组件有关系。您可以将 useMemo
视为另一个赋值语句,它有利于使用上次更新的赋值。
这意味着,useMemo
更紧急,然后是useLayoutEffect
,最后是useEffect
。