DrillLab
第 71 / 105 道71 / 105 · #339

useMemo vs useCallback

useMemo vs useCallback

先自己答,再往下看Answer it yourself first

一句话:useMemo 缓存 「函数的返回值」,useCallback 缓存 「函数本身」。两者都靠依赖数组决定要不要重算。

实际上 useCallback(fn, deps)完全等价于useMemo(() => fn, deps)——后者是前者的语法糖。 这句能答出来会加分。

缓存什么什么时候用
useMemo计算结果(值 / 对象 / 数组)① 计算真的贵(大列表排序过滤) ② 结果要当 props 传给 memo 组件 ③ 结果要当别的 hook 的依赖
useCallback函数引用① 函数要传给 memo 组件 ② 函数是 useEffect 的依赖 ③ 自定义 hook 对外暴露的函数

什么时候不该用(这半边很多人答不出):两者本身都有成本—— 要存旧值、要比较依赖。 给一个 a + buseMemo 是纯亏。「先测量,再优化」; 默认不加,profiler 显示有问题再加。

最常见的误用:包了 useCallback但依赖数组里放了每次都变的东西 —— 等于没包,还多付了比较成本。

会追问:「React 19 的编译器会怎样?」—— React Compiler 能自动插入记忆化,大部分手写的useMemo /useCallback 将不再必要。 知道这个趋势会显得你在跟进。

In one line: useMemo caches “what a function returns”, useCallback caches “the function itself”. Both use the dependency array to decide whether to recompute.

In fact useCallback(fn, deps) is exactly the same thing as useMemo(() => fn, deps) the former is sugar for the latter. Saying this scores points.

What it cachesWhen to use it
useMemoA computed result (value / object / array)① the work is genuinely expensive (sorting or filtering a big list) ② the result goes to a memo component as a prop ③ the result is a dependency of another hook
useCallbackA function reference① the function goes to a memo component ② the function is a dependency of useEffect③ the function is part of a custom hook’s public API

When not to use them — most people miss this half: both cost something — the old value is kept and the dependencies get compared. Wrapping a + b in useMemo is a pure loss. “Measure first, then optimise”: leave them out by default and add them when the profiler says so.

The most common misuse: wrapping something in useCallback but putting a value that changes every render in the dependency array — the cache never hits, and you paid for the comparison on top.

Follow-up: “What changes with the React 19 compiler?” — React Compiler can insert memoisation for you, so most hand-written useMemo and useCallback will stop being necessary. Knowing the direction shows you are keeping up.

JSX两者的区别与等价关系示意Illustrative
1// useMemo:缓存值
2const sorted = useMemo(() => items.sort(cmp), [items]);
3
4// useCallback:缓存函数
5const onPick = useCallback((id) => setPicked(id), []);
6
7// 两者的关系
8useCallback(fn, deps) === useMemo(() => fn, deps)
9
10// ✗ 常见误用:依赖每次都变,等于没缓存
11const onSave = useCallback(() => save(config), [{ ...config }]);
12// ↑ 每次都是新对象
1// useMemo caches a value
2const sorted = useMemo(() => items.sort(cmp), [items]);
3
4// useCallback caches a function
5const onPick = useCallback((id) => setPicked(id), []);
6
7// How the two relate
8useCallback(fn, deps) === useMemo(() => fn, deps)
9
10// ✗ a common mistake: the dependency changes every time, so nothing is cached
11const onSave = useCallback(() => save(config), [{ ...config }]);
12// ↑ a new object every render