文章目录
- [🚀 React `useCallback` 深入理解与实战应用](#🚀 React
useCallback深入理解与实战应用) -
- [🌱 一、`useCallback` 是什么?](#🌱 一、
useCallback是什么?) -
- [`useCallback` 是 React 提供的一个 Hook,用来**缓存函数的引用**。](#
useCallback是 React 提供的一个 Hook,用来缓存函数的引用。) - 语法
- [简而言之: **`useCallback` 返回一个记忆化的函数版本,只有当依赖改变时才会重新生成。**](#简而言之:
useCallback返回一个记忆化的函数版本,只有当依赖改变时才会重新生成。)
- [`useCallback` 是 React 提供的一个 Hook,用来**缓存函数的引用**。](#
- [⚡ 二、为什么需要 `useCallback`](#⚡ 二、为什么需要
useCallback) - [⚙️ 三、使用 `useCallback` 解决](#⚙️ 三、使用
useCallback解决) - [🔍 四、`useCallback` 与 `useMemo` 的区别](#🔍 四、
useCallback与useMemo的区别) - [🧠 五、使用时机与注意事项](#🧠 五、使用时机与注意事项)
-
- [✅ 适合使用的场景](#✅ 适合使用的场景)
-
- [1. 子组件使用了 `React.memo`,并且接收函数作为 `props`;](#1. 子组件使用了
React.memo,并且接收函数作为props;) - [2. 函数在依赖变化前后逻辑相同;](#2. 函数在依赖变化前后逻辑相同;)
- [3. 函数的重新创建会导致不必要的渲染或副作用。](#3. 函数的重新创建会导致不必要的渲染或副作用。)
- [1. 子组件使用了 `React.memo`,并且接收函数作为 `props`;](#1. 子组件使用了
- [⚠️ 不建议滥用](#⚠️ 不建议滥用)
-
- [* `useCallback` 本身也有性能开销;](#*
useCallback本身也有性能开销;) - [* 对普通函数(非 props 传递、不影响渲染)没必要使用;](#* 对普通函数(非 props 传递、不影响渲染)没必要使用;)
- [* 若滥用,会增加代码复杂度且收益有限。](#* 若滥用,会增加代码复杂度且收益有限。)
- [* `useCallback` 本身也有性能开销;](#*
- [🔧 建议](#🔧 建议)
- [💡 六、进阶技巧:搭配自定义 Hook 使用](#💡 六、进阶技巧:搭配自定义 Hook 使用)
- [🧩 七、总结](#🧩 七、总结)
- [🎯 结语](#🎯 结语)
- [🌱 一、`useCallback` 是什么?](#🌱 一、
🚀 React useCallback 深入理解与实战应用
在 React 开发中,性能优化始终是一个值得关注的话题。很多开发者在使用 Hooks 时都会遇到这样的疑问:
"为什么我需要用
useCallback?它到底解决了什么问题?什么时候该用它?"
本文将通过原理讲解 + 示例对比 + 实战技巧 ,帮助你彻底理解 React 的 useCallback。
🌱 一、useCallback 是什么?
useCallback 是 React 提供的一个 Hook,用来缓存函数的引用。
语法
js
const memoizedCallback = useCallback(
() => {
// 回调函数逻辑
},
[dep1, dep2]
);
- 第一个参数:需要缓存的回调函数。
- 第二个参数:依赖数组(当依赖变化时,才会重新创建函数)。
简而言之: useCallback 返回一个记忆化的函数版本,只有当依赖改变时才会重新生成。
⚡ 二、为什么需要 useCallback
在 React 中,每次组件重新渲染,所有内部定义的函数都会被重新创建 。
这在大多数场景下没问题,但在某些情况下会带来性能问题。
📉 示例:未使用 useCallback 的问题
代码
js
import React, { useState } from "react";
"use client";
import React, { useState } from "react";
function Child({ onClick }: { onClick: () => void }) {
console.log("🔄 Child render");
return <button onClick={onClick}>点击子组件按钮</button>;
}
const MemoChild = React.memo(Child);
function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
console.log("Clicked");
};
return (
<div>
<p>Count: {count}</p>
<div>
<button onClick={() => setCount(c => c + 1)}>加一</button>
</div>
<div>
<MemoChild onClick={handleClick} />
</div>
</div>
);
}
export default App;

分析
运行这段代码后你会发现:
👉 即使我们只更新了 count,MemoChild 仍然会重新渲染!
原因是:
- 每次
App渲染都会重新创建一个新的handleClick函数(不同内存地址); - 即使函数内容相同,但它们的引用不同;
React.memo判断props变化时发现onClick是一个新引用,于是重新渲染了子组件。
⚙️ 三、使用 useCallback 解决
代码
我们可以用 useCallback 来缓存这个函数引用:
js
const handleClick = useCallback(() => {
console.log("Clicked");
}, []);
完整代码如下:
js
"use client";
import React, { useState, useCallback } from "react";
function Child({ onClick }: { onClick: () => void }) {
console.log("🔄 Child render");
return <button onClick={onClick}>点击子组件按钮</button>;
}
const MemoChild = React.memo(Child);
function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log("Clicked");
}, []);
return (
<div>
<p>Count: {count}</p>
<div>
<button onClick={() => setCount(c => c + 1)}>加一</button>
</div>
<div>
<MemoChild onClick={handleClick} />
</div>
</div>
);
}
export default App;

分析
✅ 现在,当我们点击"加一"按钮时:
- 父组件会重新渲染;
- 但
handleClick的引用未变; - 子组件
MemoChild不再重新渲染。
性能优化达成 ✅
🔍 四、useCallback 与 useMemo 的区别
| Hook | 返回值 | 主要用途 |
|---|---|---|
useCallback(fn, deps) |
返回函数 | 缓存回调函数引用 |
useMemo(factory, deps) |
返回值 | 缓存计算结果 |
其实:
js
useCallback(fn, deps) ≡ useMemo(() => fn, deps)
但语义上更清晰:
- 当你缓存的是函数 ,用
useCallback; - 当你缓存的是计算结果 ,用
useMemo。
🧠 五、使用时机与注意事项
✅ 适合使用的场景
1. 子组件使用了 React.memo,并且接收函数作为 props;
2. 函数在依赖变化前后逻辑相同;
3. 函数的重新创建会导致不必要的渲染或副作用。
⚠️ 不建议滥用
* useCallback 本身也有性能开销;
* 对普通函数(非 props 传递、不影响渲染)没必要使用;
* 若滥用,会增加代码复杂度且收益有限。
🔧 建议
先写出正确的代码,再通过性能分析(如 React DevTools Profiler)决定是否引入
useCallback。
💡 六、进阶技巧:搭配自定义 Hook 使用
在自定义 Hook 中使用 useCallback,可以有效避免无限循环:
js
function useFetch(url) {
const [data, setData] = useState(null);
const fetchData = useCallback(async () => {
const res = await fetch(url);
const json = await res.json();
setData(json);
}, [url]);
useEffect(() => {
fetchData();
}, [fetchData]);
return data;
}
这里若不加 useCallback,fetchData 每次都会变,useEffect 就会无限执行。
在这个代码中,fetchData 会因为 url 变化而变化。具体来说:
- useCallback 的依赖项是 [url],所以当 url 发生变化时,fetchData 函数会重新创建
- useEffect 的依赖项是 [fetchData],所以当 fetchData 变化时,useEffect 会重新执行
- 当 useEffect 重新执行时,它会调用新的 fetchData 函数,从而触发新的 API 请求
🧩 七、总结
| 项目 | 说明 |
|---|---|
| 作用 | 缓存函数引用,避免子组件重复渲染 |
| 语法 | useCallback(fn, deps) |
| 适用场景 | 函数作为 props 传递给子组件时 |
| 常见搭配 | React.memo、useEffect |
| 不要滥用 | 若无性能瓶颈,使用纯函数更简单 |
🎯 结语
useCallback 并不是一个"必须用"的 Hook,而是在特定性能场景下的优化工具 。
理解它的核心思路------函数引用的稳定性,才能在正确的地方使用它。