1. 使用 useMemo
还是 useCallback
的判断依据
useMemo
用于计算值:当计算值是一个复杂的操作,例如涉及大量数据的处理、复杂的算法运算等,且这个计算值在依赖不变的情况下不应该重新计算时,使用 useMemo
。它会返回一个 memoized 值,只有在其依赖数组中的值发生变化时,才会重新计算。例如,计算一个大型数组的总和,每次父组件更新时都重新计算这个总和是不必要的,使用 useMemo
可以避免这种不必要的计算。
const sum = useMemo(() => {
let total = 0;
for (let i = 0; i < largeArray.length; i++) {
total += largeArray[i];
}
return total;
}, [largeArray]);
useCallback
用于函数:当子组件依赖一个函数引用,并且希望在依赖不变时函数引用不发生变化,使用 useCallback
。这在将函数作为 prop 传递给子组件,且子组件依赖于函数引用的稳定性(例如 shouldComponentUpdate
判断,或者 React.memo
依赖函数引用相等性来决定是否重新渲染)时非常有用。例如,子组件内部有一个按钮,点击按钮调用传递进来的函数,如果每次父组件更新都导致函数引用变化,可能会触发不必要的子组件重新渲染。
const handleClick = useCallback(() => {
// 处理点击逻辑
}, []);
2. 错误使用导致的性能问题
- 错误使用
useMemo
包裹函数:
useMemo
返回的是一个值,虽然函数也是值的一种,但它的本意并非用于 memoize 函数引用。如果错误使用,函数每次调用都需要先从 memoized 值中获取,这可能引入不必要的间接性。
- 由于
useMemo
旨在缓存计算值,它的依赖数组检查可能与 useCallback
不同。如果依赖设置不当,可能导致函数过早或过晚更新,从而破坏组件的逻辑,例如导致子组件依赖的函数引用变化不及时,影响交互逻辑。
- 错误使用
useCallback
包裹计算值:
useCallback
主要用于 memoize 函数引用,它不会像 useMemo
那样缓存计算结果。所以每次组件渲染时,都会重新执行包裹的表达式来获取值,这就失去了缓存复杂计算结果的意义,导致性能损耗,例如每次渲染都重新计算大型数组的总和,而不是复用之前的计算结果。
- 此外,
useCallback
返回的是函数,若将其当作普通值使用,可能会在使用时需要额外的函数调用操作,造成代码逻辑的混乱和潜在的性能问题。