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 来"冻结"函数引用。
相关推荐
huangdong_4 分钟前
京东商品图片视频批量下载与m3u8视频合并技术完整实现方案
大数据·前端·数据库
尽兴-8 分钟前
4.1 智能体核心:Agent、Sub-Agent、ReAct、规划执行
前端·javascript·react.js·agent·react·subagent
小小小小宇21 分钟前
前端 Shadow DOM 全解析与应用
前端
万物更新_24 分钟前
vue框架
前端·javascript·vue.js·笔记
小小小小宇26 分钟前
前端 Web Workers 和 Service Workers 全解析与应用
前端
陆枫Larry42 分钟前
浏览器的 Reflow 和 Repaint 是什么?为什么要尽量避免它们?
前端
孜孜不倦不忘初心44 分钟前
mac安装nvm及问题记录
前端·node.js
Richar1 小时前
Object.freeze()注意事项
前端·javascript
TA远方1 小时前
【HTML】JavaScript Canvas 图像截取与保存完整指南
前端·javascript·html·canvas·截图·截取
Asize1 小时前
JavaScript 数据类型解析:从 null 与 undefined 的迷思到栈堆内存真相
前端·javascript·面试