React中useMemo与useCallback的区别及官方等价表述疑问
useMemo 与 useCallback 核心差异拆解
官方等价表述的本质
文档提到的
useCallback(fn, deps)等价于useMemo(() => fn, deps)没有任何歧义,核心是两个Hook的设计目标差异:
useCallback从设计之初就是专门用来缓存函数引用的:它会把你传入的函数本身作为返回值做缓存,只要依赖不变化,就不会生成新的函数引用。它的内部实现本质就是对useMemo的一层薄封装:// 简化版 useCallback 实现逻辑 function useCallback(callback, deps) { return useMemo(() => callback, deps) }useMemo的定位是缓存任意类型的计算结果:它的入参是一个纯计算函数,React只会执行这个计算函数,把函数的返回值作为缓存内容存起来——它完全不关心这个返回值是数字、对象、字符串,还是函数。
关于“useMemo需要传入返回函数的函数”的理解
你的感知完全没有偏差,这只是useMemo通用规则下的自然结果:
- 按照useMemo的通用执行逻辑,如果你想让useMemo帮你缓存一个函数,那你传入的计算函数的返回值,就必须是你要缓存的那个函数——这也是官方等价式里写
() => fn的原因:外层的箭头函数执行完,返回的就是你要缓存的fn本身。 - 你完全可以用useMemo实现函数缓存,只是写法上比useCallback多一层嵌套:
// 用useMemo缓存函数,效果和useCallback完全一致 const clickHandler = useMemo(() => { // 可以在这里写前置的准备逻辑 return () => { console.log("按钮被点击") } }, []) // 完全等价的useCallback写法,代码更简洁 const clickHandler2 = useCallback(() => { console.log("按钮被点击") }, []) - 反过来推也成立:如果给useMemo传的计算函数返回的是数字、字符串,那它就缓存这些值,和函数没有任何关系。
被缓存的函数的调用时机
这个问题不需要绑定特殊的规则,把useMemo的流程拆解开就完全清楚:
- useMemo的计算函数只会在两个时机被执行:首次组件渲染时,以及依赖项发生变化触发重渲染时。这个过程是React在渲染流程里自动完成的,和用户的交互逻辑完全无关。
- 那个被计算函数返回、被缓存起来的函数本身,什么时候触发完全由你的业务代码决定:可以绑定到按钮点击事件上由用户触发,可以作为参数传给子组件被内部调用,也可以是副作用清理函数里主动调用——React本身根本不会主动调用这个函数,它只是帮你存了这个函数的引用而已。
- 举个完整的执行流程:
- 第一次渲染时,React执行你传给useMemo的外层计算函数
() => fn,执行完拿到返回值fn,把这个fn存在组件对应的Hook存储空间里,返回给你使用。 - 后续任意一次重渲染,React先对比依赖数组有没有变化,如果没有变化,直接把之前存好的
fn返回给你,根本不会重新执行那个外层的() => fn函数。 - 你拿到这个
fn之后,怎么调用它完全和普通函数没有区别——你平时什么时候调用自己写的函数,就什么时候调用它,React不会干预。
- 第一次渲染时,React执行你传给useMemo的外层计算函数
两者的实际使用边界
useCallback是专门为函数缓存场景设计的语法糖:写出来的代码没有多余的嵌套,语义清晰,不会出现“我现在缓存的到底是值还是函数”的歧义。useMemo是通用的缓存工具:适合缓存计算出来的数组、对象、基础类型值,如果用它来缓存函数,会写出没必要的嵌套代码,属于绕远路。- 不存在“useMemo缓存的函数有特殊行为”的说法——所有被缓存的内容都是普通JavaScript值,函数就是普通函数,没有任何特殊的执行规则。
内容的提问来源于stack exchange,提问作者Dean Hiller
相关产品推荐
相关产品推荐

