今天我们要聊一个听起来“高大上”,其实特别实用的 Hook —— useCallback
。
不仅如此,这一节也是前 16 天内容的 Hooks 综合实战回顾,我们将一起动手,构建一个「功能丰富」的组件,让 useState、useEffect、useRef、useMemo、useCallback 全部上场!
简单说:记住一个函数,别让它每次组件重新渲染时都“变脸”。
在 JS 中,函数是“引用类型”,每次函数组件执行(也就是重新渲染),函数定义也会重新生成。即使你写的是一样的函数,它也是一个“新函数”。
useCallback 的目的:如果依赖没变,就不要重新生成这个函数。
想象下面这个场景👇
你有一个父组件,传了个 handleClick
函数给子组件。子组件用 React.memo
包了起来,想让它只在 props 变了的时候重新渲染。
结果你每次渲染都传了一个“新函数”,子组件每次都重新渲染 —— 优化白做。
这时候就要用 useCallback 👇
const handleClick = useCallback(() => {
console.log('Clicked!');
}, []);
空依赖数组代表:这个函数只会在初次渲染时生成一次,之后每次渲染都复用它。
const memoizedFn = useCallback(() => {
doSomething(a, b);
}, [a, b]);
a
或 b
改变时,这个函数才会被重新生成📌 和 useMemo
很像,但 useMemo
是记住“值”,useCallback
是记住“函数”。
搭配 React.memo
,useCallback
的作用才最明显:
const Parent = () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Clicked!');
}, []);
return <Child onClick={handleClick} />;
};
const Child = React.memo(({ onClick }) => {
return <button onClick={onClick}>Click me</button>;
});
✅ 如果不用 useCallback,每次渲染 handleClick 都变,Child 也重渲染。
✅ 如果用了 useCallback,函数是稳定引用,Child 就不会白白更新。
特性 | useMemo | useCallback |
---|---|---|
记忆的对象 | 一个值/计算结果 | 一个函数引用 |
使用场景 | 缓存昂贵计算 | 缓存事件处理函数/回调 |
返回值 | 值 | 函数 |
小贴士:用 useCallback 相当于 useMemo(() => fn, deps)
的语法糖。
🛠️ 任务:写一个「可加载数据的计数器组件」
useState
管理 count 和 loading 状态useEffect
模拟数据加载useRef
聚焦按钮useMemo
计算派生数据(例如是否是偶数)useCallback
创建事件处理函数(比如按钮点击)👉 这个练习就是我们 17 天的成果大汇总!可以用这个项目当作“毕业作业”。
Hook | 作用 |
---|---|
useState | 最基础的状态管理 |
useReducer | 管理复杂/结构化状态 |
useEffect | 副作用管理:数据请求、订阅、DOM 操作等 |
useRef | 保留可变值、不触发渲染;也可以获取 DOM |
useMemo | 缓存计算结果,避免重复计算 |
useCallback | 缓存函数引用,避免函数“变脸” |
✨ 全都掌握了吗?可以在项目中任意组合了吗?能记得啥时候该用谁了吗?
✅ 适合:
❌ 不适合:
🔔 记住一句话:优化是有成本的,用在对的场景才有价值。