react笔记之useCallback

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


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

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

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

  1. 传递给 React.memo 包裹的子组件 → 函数引用变化会导致子组件即使 props 未实质改变也重新渲染。
  2. 作为依赖项传给 useEffect、useMemo 等 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 不重渲染 ✅
点击按钮 正常工作 正常工作

因为 Child 用 React.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 来"冻结"函数引用。
相关推荐
三84411 小时前
TLS + Web API 安全 · 05 · JWT 与 OAuth2 攻防
前端·安全
摇滚侠11 小时前
《On Java 中文版 基础卷》阅读笔记 安装 Java 和本书示例 02
java·开发语言·笔记
zhongwei22811 小时前
vba将一个文件夹的内容复制到另外一个文件夹的函数,若存在则是否覆盖的参数。复制前检查源文件夹是否存在。若目标文件夹不存在,则新建
前端·vba
怕浪猫13 小时前
分享一个做视频的skill,这条白板视频,每一笔都是代码画的
前端·javascript·面试
咖啡忍者13 小时前
【SAP BASIS】Section 5: SAP System Configuration
笔记
aa小小13 小时前
大屏自适应缩放方案
前端·数据可视化
陪我去看海14 小时前
被要求猛出小程序,做了这个多平台多环境的部署替我承受压力
前端·微信小程序·抖音小程序
Frag0ut14 小时前
告别插件时代:HTML5 视频播放对比 Flash/Silverlight/ActiveX 的性能与技术优势解析
前端·html5·视频播放·flash·activex·流媒体技术·silverlight
用户8186180280014 小时前
MongoDB文档模型设计——从建模到索引
前端
無名路人15 小时前
小程序点餐页吸顶滚动之分类按需加载,上划切换
前端·vue.js·微信小程序