看官方的文档介绍这两个 api 的概念
useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
返回一个 memoized 值。
1、把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。 2、如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。
useCallback
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
返回一个 memoized 回调函数。
把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。
看 TS 类型
function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
function useCallback<T extends Function>(callback: T, deps: DependencyList): T;
- 泛型 T 在 useCallback 中是一个钩子函数
- 在 useMemo 中是一个返回值。
总结
返回的值类型不同
- useMemo 返回的是计算值
- useCallback 返回的是一个回调函数
来个 demo
import React, { FC, useCallback, useMemo, useState } from 'react';
const Index: FC = (props) => {
const [count, setCount] = useState(0);
const isEvenNumber = useMemo(() => {
return count % 2 === 0;
}, [count]);
const onClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<div>{count} is {isEvenNumber ? 'even':'odd'} number</div>
<button onClick={onClick}></button>
</div>
);
};
useCallback 和 useMemo 的参数都是一个函数加一个依赖数组,依赖没有改变时直接返回内存中缓存的结果,无需重复计算。简单理解就是 useCallback 缓存事件处理函数,useMemo 缓存二次计算的结果,如上面的点击事件,以及通过 count 值判断奇数偶数的二次计算结果。
本质原因
React 的函数组件是非常好用的东西,相比 class 写法以及 Vue 的对象挂载写法简洁很多,代码测试复用成本低,容易入手,但也带来一些问题,无状态函数很理想,但现实有一些计算开销大、组件渲染频繁的场景是需要状态的,每次都计算一遍状态(callback 和 二次计算值)无疑很浪费内存,函数不像对象(React class 写法或者 Vue 组件写法)可以直接将状态挂载在自身,没有浪费内存的问题,要实现类似的效果只能找一个的内存挂载点挂载这些东东,所以有了 useCallback 和 useMemo 这些 hook。
|