react笔记之useCallback

useCallback 是 React 中的一个 Hook,用于缓存函数(callback)的引用 ,避免在组件每次重新渲染时都创建一个新的函数实例


🎯 核心目的:防止不必要的子组件重渲染或重复注册副作用

在 React 中,函数也是值。每次组件重新渲染时,如果直接写 () => {...}function() {...},都会生成一个全新的函数对象。即使函数内容完全一样,JavaScript 也会认为它们是不同的(因为引用不同)。

这在以下场景会带来问题:

  1. 传递给 React.memo 包裹的子组件 → 函数引用变化会导致子组件即使 props 未实质改变也重新渲染。
  2. 作为依赖项传给 useEffectuseMemo 等 Hook → 函数变化会触发不必要的副作用或重新计算。

useCallback 就是用来解决这个问题的。


🔧 语法

js 复制代码
const memoizedCallback = useCallback(
  () => {
    // 函数体
  },
  [dependencies] // 依赖数组
);
  • 只有当依赖数组中的值发生变化时,才会返回一个新的函数
  • 否则,返回之前缓存的函数引用

💡 useCallback(fn, deps) 等价于 useMemo(() => fn, deps)


🌰 举例说明

场景:父组件向子组件传递一个回调函数
js 复制代码
import React, { useState, useCallback } from 'react';

// 子组件用 React.memo 包裹,只有 props 变化才重渲染
const Child = React.memo(({ onIncrement }) => {
  console.log('Child 渲染了');
  return <button onClick={onIncrement}>点我+1</button>;
});

function Parent() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('');

  // ❌ 没用 useCallback:每次 Parent 渲染都会创建新函数
  // const handleIncrement = () => setCount(c => c + 1);

  // ✅ 使用 useCallback 缓存函数
  const handleIncrement = useCallback(() => {
    setCount(c => c + 1);
  }, []); // 无依赖,只创建一次

  return (
    <div>
      <p>Count: {count}</p>
      <p>
        Name: <input value={name} onChange={e => setName(e.target.value)} />
      </p>
      {/* 传递回调给子组件 */}
      <Child onIncrement={handleIncrement} />
    </div>
  );
}
行为对比:
操作 不用 useCallback 用了 useCallback
修改 name(输入框) Parent 重渲染 → handleIncrement 是新函数 → Child 重渲染(尽管 onIncrement 逻辑没变) handleIncrement 引用不变 → Child 不重渲染
点击按钮 正常工作 正常工作

因为 ChildReact.memo 包裹了,它会浅比较 props。如果 onIncrement 引用变了,就认为 props 变了,于是重渲染。


⚠️ 注意事项

  1. 不要滥用

    如果子组件没有用 React.memo,或者函数不是传给子组件/副作用,那用 useCallback 反而增加开销。

  2. 正确声明依赖

    如果回调里用了状态或 props,必须加到依赖数组中,否则会捕获旧值(stale closure):

    js 复制代码
    const [count, setCount] = useState(0);
    const [step, setStep] = useState(1);
    
    // ❌ 错误:没把 step 加入依赖,永远用初始 step=1
    const increment = useCallback(() => {
      setCount(c => c + step); // step 可能是过期的!
    }, []); // ← 缺少 step
    
    // ✅ 正确
    const increment = useCallback(() => {
      setCount(c => c + step);
    }, [step]);
  3. 它缓存的是"函数引用",不是"执行结果"

    • useMemo → 缓存
    • useCallback → 缓存函数本身

✅ 什么时候该用?

场景 建议使用 useCallback
把函数传给 React.memo 的子组件 ✅ 是
函数作为 useEffect / useMemo 的依赖 ✅ 是
函数只在当前组件内部调用,不传出去 ❌ 否
组件很小,性能不敏感 ❌ 通常不需要

总结一句话:

useCallback 用于稳定函数引用,避免因函数"看似变化"而导致的不必要渲染或副作用,尤其在配合 React.memo 时非常有用。

如果你熟悉 Vue,可以类比:

  • Vue 的方法(methods)天然不会在每次渲染时重建(因为绑定在组件实例上),所以一般不需要类似优化;
  • 而 React 的函数组件每次执行都会重新定义内部函数,所以需要 useCallback 来"冻结"函数引用。
相关推荐
GIS之路21 小时前
ArcPy,一个基于 Python 的 GIS 开发库简介
前端
可夫小子1 天前
OpenClaw基础-为什么会有两个端口
前端
喝拿铁写前端1 天前
Dify 构建 FE 工作流:前端团队可复用 AI 工作流实战
前端·人工智能
喝咖啡的女孩1 天前
React 合成事件系统
前端
从文处安1 天前
「九九八十一难」组合式函数到底有什么用?
前端·vue.js
用户5962585736061 天前
戴上AI眼镜逛花市——感受不一样的体验
前端
yuki_uix1 天前
Props、Context、EventBus、状态管理:组件通信方案选择指南
前端·javascript·react.js
老板我改不动了1 天前
前端面试复习指南【代码演示多多版】之——HTML
前端
panshihao1 天前
Mac 环境下通过 SSH 操作服务器,完成前端静态资源备份与更新(全程实操无坑)
前端
hulkie1 天前
从 AI 对话应用理解 SSE 流式传输:一项 "老技术" 的新生
前端·人工智能