一、先理解核心问题
React 组件为什么会重复渲染?
因为:
父组件重新执行 → 子组件默认重新执行
哪怕 props 没变。
而且:
- 每次 render 都会重新创建函数
- 每次 render 都会重新创建对象
- 每次 render 都会重新执行计算
这就可能导致:
- 子组件无意义重新渲染
- 复杂计算重复执行
- 性能浪费
于是有了:
useCallback → 缓存函数
useMemo → 缓存计算结果
二、useCallback 是什么?
缓存“函数引用”
场景:避免子组件重复渲染
看例子:
function Parent() {
const [count, setCount] = useState(0)
const handleClick = () => {
console.log("clicked")
}
return <Child onClick={handleClick} />
}
问题:
每次 Parent render:
handleClick = new Function()
即使逻辑没变,函数地址变了。
如果子组件是:
const Child = React.memo(({ onClick }) => {
console.log("child render")
return <button onClick={onClick}>Click</button>
})
它仍然会重新渲染,因为:
旧函数 !== 新函数
用 useCallback
const handleClick = useCallback(() => {
console.log("clicked")
}, [])
现在函数引用不会变。
子组件不会重复 render。
三、useMemo 是什么?
缓存“计算结果”
场景:复杂计算避免重复执行
function App({ list }) {
const sortedList = list.sort() // 每次 render 都执行
return <div>{sortedList.length}</div>
}
如果 list 很大,每次都排序很浪费。
用 useMemo
const sortedList = useMemo(() => {
return list.sort()
}, [list])
只有 list 变了才重新计算。
四、核心区别
| useCallback | useMemo | |
|---|---|---|
| 缓存什么 | 函数 | 计算结果 |
| 返回值 | 函数 | 任意值 |
| 本质 | useMemo 的语法糖 | 底层实现 |
实际上:
useCallback(fn, deps)
等价于:
useMemo(() => fn, deps)
五、什么时候需要 useCallback?
✅ 1. 传给 React.memo 子组件的函数
<Child onClick={handleClick} />
子组件用 React.memo 包裹时。
✅ 2. 传给 useEffect 依赖数组
useEffect(() => {
doSomething()
}, [handleClick])
如果 handleClick 每次变,effect 每次触发。
✅ 3. 大量列表中的事件函数
例如 1000 行表格,每行一个按钮。
六、什么时候需要 useMemo?
✅ 1. 复杂计算
- 排序
- 过滤
- 映射
- 数据处理
✅ 2. 生成大对象
const config = useMemo(() => ({
headers: {...},
timeout: 5000
}), [])
避免对象引用变化导致子组件刷新。
✅ 3. 防止依赖变化
例如:
useEffect(() => {
...
}, [config])
如果 config 是新对象,每次都触发。
七、什么时候不要用(非常重要)
很多人滥用。
❌ 1. 简单函数不需要
const add = () => a + b
没必要缓存。
❌ 2. 组件不使用 React.memo
如果子组件本身每次都会渲染,useCallback 没意义。
❌ 3. 计算很轻量
简单 map 不需要 useMemo。
八、性能误区
⚠ useCallback / useMemo 不是性能优化万能钥匙。
它们:
- 也有性能成本
- 也要比较依赖
- 也会增加复杂度
React 官方建议:
先写清晰代码
真有性能问题再优化
九、实际开发经验(你这个水平该知道)
真正有用场景:
- 大型表格
- 数据平台
- 编辑器
- 可视化平台
- 高频状态更新
基础业务系统:
基本用不到。
十、面试标准回答
可以这样说:
useCallback 用于缓存函数引用,主要用于避免因为函数重新创建导致的子组件不必要渲染,通常配合 React.memo 使用。
useMemo 用于缓存计算结果,避免复杂计算在每次渲染时重复执行。
两者本质上 useCallback 是 useMemo 的语法糖。
它们属于性能优化手段,不应该滥用,只有在存在性能瓶颈或引用稳定性需求时才使用。
十一、再给你进阶一点
React 18 之后:
- 大多数小组件没必要手动优化
- React Compiler 未来会自动优化
- 过度 useCallback 反而降低可读性
一句话总结
useCallback → 缓存函数
useMemo → 缓存值
核心目的 → 控制引用稳定 + 避免重复计算