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 来"冻结"函数引用。
相关推荐
柳杉4 分钟前
从动漫水面到赛博飞船:这位开发者的Three.js作品太惊艳了
前端·javascript·数据可视化
C羊驼7 分钟前
C语言:两天打鱼,三天晒网
c语言·经验分享·笔记·算法·青少年编程
Greg_Zhong28 分钟前
前端基础知识实践总结,每日更新一点...
前端·前端基础·每日学习归类
sheeta199829 分钟前
苍穹外卖Day04笔记
笔记
We་ct1 小时前
LeetCode 148. 排序链表:归并排序详解
前端·数据结构·算法·leetcode·链表·typescript·排序算法
IT_陈寒1 小时前
JavaScript开发者必看:5个让你的代码性能翻倍的隐藏技巧
前端·人工智能·后端
2501_921930831 小时前
ReactNative项目OpenHarmony三方库集成实战:react-native-appearance(更推荐自带的Appearance)
javascript·react native·react.js
还是大剑师兰特2 小时前
Vue3 中 computed(计算属性)完整使用指南
前端·javascript·vue.js
井川不擦2 小时前
前端安全通信方案:RSA + AES 混合加密
前端
孜孜不倦不忘初心2 小时前
Ant Design Vue 表格组件空数据统一处理 踩坑
前端·vue.js·ant design