🧠 一句话解释:
useCallback(fn, deps) 会返回一个 “记住的函数”,只有当依赖项 deps 变化时,才重新创建函数。
这样可以避免每次 render 都生成新的函数引用,从而优化性能、避免子组件重复渲染。
✅ 基本语法
const memoizedFn = useCallback(() => {
// 函数体
}, [依赖1, 依赖2]);
疑问:
✅ 为什么要用 useCallback?
在 React 中,每次组件渲染时,函数会重新创建:
const handleClick = () => {
console.log('clicked');
};
每次组件更新时 handleClick 是 新函数,引用变了!
如果你把这个函数传给 React.memo 包裹的子组件:
<Child onClick={handleClick} />
即使 props 看起来没变,因为函数是新引用,子组件也会重新渲染。
✅ 解决方案:
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
🧪 实战例子
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount((c) => c + 1);
/*
等价于
setCount((prevCount) => {
return prevCount + 1;
});
因为每次拿到的都是最新的值
*/
}, []); // 不依赖外部变量(闭包中只用 setCount)
return <button onClick={handleClick}>加1</button>;
✅ 何时用 useCallback?
情况------------------------------------------------------|-------------是否推荐用 useCallback
——————————————————————————————————
👎 普通函数只在组件内部使用--------------------|--------------------❌ 不需要
——————————————————————————————————
✅ 函数传给 React.memo 的子组件-------------|--------------------✅ 推荐
——————————————————————————————————
✅ 函数用作 useEffect 的依赖 --------------------|--------------------✅ 推荐
——————————————————————————————————
✅ 函数在频繁渲染的组件中反复定义 ----------|--------------------✅ 推荐
——————————————————————————————————
✅ 对比总结
Hook---------------------------------------------|---------用途
——————————————————————————————————
useState----------------------------------------|-----管理组件内部状态
——————————————————————————————————
useEffect---------------------------------------|------处理副作用(如请求、订阅)
——————————————————————————————————
useRef------------------------------------------|---保存可变值但不引起刷新
——————————————————————————————————
useMemo--------------------------------------|------缓存变量(函数返回值)
——————————————————————————————————
useCallback-----------------------------------|--------缓存函数(避免重新创建函数)
——————————————————————————————————
🧠 总结一句话
useCallback 是用来缓存“函数”的 Hook,特别适合传给子组件或用作依赖,能有效避免不必要的渲染或副作用。
再简单的理解
✅ 用不用的判断方法(秒懂版)
✅ 把函数传给子组件----------------------------需要----------------------------避免子组件无谓刷新
✅ 函数依赖某些值,但你希望它只在值变化时重建----------------------------需要----------------------------保持函数稳定
🚫 函数只在当前组件里用----------------------------不需要----------------------------没有性能问题
🚫 函数不参与复杂逻辑----------------------------不需要----------------------------没必要增加代码复杂度
🧠useCallback那什么时候用了值 但是还可以不加依赖
✅ 合法不加依赖的 3 个常见场景:
✅ 1. 使用函数式更新,不依赖外部变量
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount(prev => prev + 1); // ✅ 使用了 setState 函数式更新,不依赖外部 count
}, []); // 这里不需要加 count
✅ 2. 值来自 ref,不需要加依赖
const countRef = useRef(0);
const logCount = useCallback(() => {
console.log(countRef.current); // ✅ 引用的是 ref,不参与依赖更新
}, []);
📌 说明:
• ref.current 始终是最新值;
• 它不会触发重新渲染;
• 使用 ref 的场景下,不需要加到依赖数组。
✅ 3. 你“只想执行一次”的副作用,故意不加依赖
useEffect(() => {
console.log('只执行一次');
}, []); // ✅ 故意不加依赖
📌 说明:
• 很多初始化操作只需执行一次(如加载配置、初始化定位);
• 即使用到了外部变量,如果它不会变,这样写也可以;
• 如果 ESLint 警告你,你可以加注释忽略它:
❗什么时候「用了值却不加依赖」会出错?
useEffect(() => {
if (user.isAdmin) {
doSomething();
}
}, []); // ❌ user 其实可能会变
