最后更新:2026-01-10
useCallback 和 useMemo 是两个很容易被滥用的 Hook。
我见过不少项目代码,几乎每个函数定义都套了 useCallback,每个计算都套了 useMemo,理由是”优化性能”。结果代码变复杂了,性能也没什么实际改善,反而因为依赖项没填对造成了 bug。
说说我自己是怎么理解这两个的。
它们的作用是”缓存”,不是”优化”
先从根上搞清楚:React 函数组件每次 render 都会重新执行,函数体里所有的变量和函数定义都会重新创建。
1 | function App() { |
useCallback 的作用是在依赖项不变的情况下,返回同一个函数引用:
1 | const handleClick = useCallback(() => { |
useMemo 类似,缓存的是值(计算结果):
1 | const result = useMemo(() => { |
但这里有个关键问题:缓存本身也有开销。React 需要存储之前的值、比较依赖项。如果你缓存的东西不值这个开销,用了反而变慢。
什么情况下用 useCallback 才有意义
场景一:子组件使用了 React.memo
1 | // 子组件用 memo 包裹,只有 props 变了才重新渲染 |
这才是 useCallback 真正有价值的场景:配合 React.memo 避免不必要的子组件渲染。
如果子组件没有用 memo,父组件每次 render 它都会重新渲染,useCallback 没有任何意义。
场景二:作为其他 Hook 的依赖
1 | function useAutoFetch(fetchFn) { |
这种情况函数作为 useEffect、useCallback 等的依赖项时,需要保证引用稳定。
什么情况下用 useMemo 才有意义
场景一:开销大的计算
1 | function DataTable({ rawData, filters }) { |
这里如果 rawData 和 filters 没变,就不需要重新计算,用 useMemo 合理。
但如果数据量很小(几十条),过滤排序本来就很快,用 useMemo 反而多了依赖比较的开销。
场景二:稳定对象引用,配合 memo
1 | function Child({ config }) { ... } |
原理和 useCallback 类似,只是缓存的是对象而不是函数。
最常见的错误用法
错误一:没有 memo 的子组件加 useCallback
1 | // Child 没有用 React.memo |
useCallback 保证了引用稳定,但子组件没有 memo,根本不检查 props 是否变化,照样重新渲染。白写了。
错误二:依赖项写错
1 | const [keyword, setKeyword] = useState('') |
这类 bug 很隐蔽,结果表现是”搜索功能不生效”,排查半天找不到原因。
错误三:useMemo 处理简单计算
1 | // 完全没必要,a + b 本来就是纳秒级的 |
我现在的使用原则
- 默认不用,遇到性能问题再考虑
- 用
useCallback前先问:子组件有没有用React.memo?这个函数有没有作为其他 Hook 的依赖? - 用
useMemo前先问:这个计算真的耗时吗?能不能直接算? - 依赖项一定要填完整,用 eslint-plugin-react-hooks 帮你检查
说实话,大多数应用的性能瓶颈不在组件重新渲染上,而在网络请求、大列表渲染等地方。盲目用这两个 Hook,代码变复杂,bug 变多,收益却微乎其微。用之前先 profiling,找到真正的瓶颈再优化。





