useMemo Versus useCallback

The two Hooks have the same signature and the same dependency rules, and they differ in one thing: what gets cached. useMemo runs your function and caches what it returned. useCallback caches the function itself and never runs it. That makes useCallback(fn, deps) nothing more than shorthand:

useCallback expressed with useMemoJavaScript
const handleSave = useCallback(() => save(draft), [draft]);
const handleSave = useMemo(() => () => save(draft), [draft]);   // exactly the same thing

The double arrow is the whole story: useMemo needs a function that returns the function you want to keep. useCallback exists because that shape is easy to mistype and hard to read.

The one difference between the two memoization Hooks
useMemo useCallback
What is cached The return value The function object
Is your function called? Yes, when a dependency changed Never by the Hook
Typical use Derived data, filtered arrays, JSX Event handlers, Effect dependencies
Returns on a cache hit Last computed value Last function, same address

The mistake this produces is writing useCallback(() => expensiveTotal(rows), [rows]) and expecting the total to be cached. It is not: you cached the wrapper, and expensiveTotal still runs in full whenever something calls it. Anything whose result you want to reuse belongs in useMemo — which can also do everything useCallback does, including keeping a whole object of handlers stable for a context value (Context Performance Pitfalls).