React useCallback介绍(用来缓存函数的引用,避免每次渲染都重新创建函数)主要用于性能优化

文章目录

  • [🚀 React `useCallback` 深入理解与实战应用](#🚀 React useCallback 深入理解与实战应用)
    • [🌱 一、`useCallback` 是什么?](#🌱 一、useCallback 是什么?)
      • [`useCallback` 是 React 提供的一个 Hook,用来**缓存函数的引用**。](#useCallback 是 React 提供的一个 Hook,用来缓存函数的引用。)
      • 语法
      • [简而言之: **`useCallback` 返回一个记忆化的函数版本,只有当依赖改变时才会重新生成。**](#简而言之: useCallback 返回一个记忆化的函数版本,只有当依赖改变时才会重新生成。)
    • [⚡ 二、为什么需要 `useCallback`](#⚡ 二、为什么需要 useCallback)
      • [📉 示例:未使用 `useCallback` 的问题](#📉 示例:未使用 useCallback 的问题)
    • [⚙️ 三、使用 `useCallback` 解决](#⚙️ 三、使用 useCallback 解决)
    • [🔍 四、`useCallback` 与 `useMemo` 的区别](#🔍 四、useCallbackuseMemo 的区别)
    • [🧠 五、使用时机与注意事项](#🧠 五、使用时机与注意事项)
      • [✅ 适合使用的场景](#✅ 适合使用的场景)
        • [1. 子组件使用了 `React.memo`,并且接收函数作为 `props`;](#1. 子组件使用了 React.memo,并且接收函数作为 props;)
        • [2. 函数在依赖变化前后逻辑相同;](#2. 函数在依赖变化前后逻辑相同;)
        • [3. 函数的重新创建会导致不必要的渲染或副作用。](#3. 函数的重新创建会导致不必要的渲染或副作用。)
      • [⚠️ 不建议滥用](#⚠️ 不建议滥用)
        • [* `useCallback` 本身也有性能开销;](#* useCallback 本身也有性能开销;)
        • [* 对普通函数(非 props 传递、不影响渲染)没必要使用;](#* 对普通函数(非 props 传递、不影响渲染)没必要使用;)
        • [* 若滥用,会增加代码复杂度且收益有限。](#* 若滥用,会增加代码复杂度且收益有限。)
      • [🔧 建议](#🔧 建议)
    • [💡 六、进阶技巧:搭配自定义 Hook 使用](#💡 六、进阶技巧:搭配自定义 Hook 使用)
    • [🧩 七、总结](#🧩 七、总结)
    • [🎯 结语](#🎯 结语)

🚀 React useCallback 深入理解与实战应用

在 React 开发中,性能优化始终是一个值得关注的话题。很多开发者在使用 Hooks 时都会遇到这样的疑问:

"为什么我需要用 useCallback?它到底解决了什么问题?什么时候该用它?"

本文将通过原理讲解 + 示例对比 + 实战技巧 ,帮助你彻底理解 React 的 useCallback


🌱 一、useCallback 是什么?

useCallback 是 React 提供的一个 Hook,用来缓存函数的引用

语法

js 复制代码
const memoizedCallback = useCallback(
  () => {
    // 回调函数逻辑
  },
  [dep1, dep2]
);
  • 第一个参数:需要缓存的回调函数。
  • 第二个参数:依赖数组(当依赖变化时,才会重新创建函数)。

简而言之: useCallback 返回一个记忆化的函数版本,只有当依赖改变时才会重新生成。


⚡ 二、为什么需要 useCallback

在 React 中,每次组件重新渲染,所有内部定义的函数都会被重新创建

这在大多数场景下没问题,但在某些情况下会带来性能问题。

📉 示例:未使用 useCallback 的问题

代码
js 复制代码
import React, { useState } from "react";

"use client";

import React, { useState } from "react";

function Child({ onClick }: { onClick: () => void }) {
  console.log("🔄 Child render");
  return <button onClick={onClick}>点击子组件按钮</button>;
}

const MemoChild = React.memo(Child);

function App() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    console.log("Clicked");
  };

  return (
    <div>
      <p>Count: {count}</p>
      <div>
        <button onClick={() => setCount(c => c + 1)}>加一</button>
      </div>
      <div>
        <MemoChild onClick={handleClick} />
      </div>
    </div>
  );
}

export default App;
分析

运行这段代码后你会发现:

👉 即使我们只更新了 countMemoChild 仍然会重新渲染!

原因是:

  • 每次 App 渲染都会重新创建一个新的 handleClick 函数(不同内存地址);
  • 即使函数内容相同,但它们的引用不同
  • React.memo 判断 props 变化时发现 onClick 是一个新引用,于是重新渲染了子组件。

⚙️ 三、使用 useCallback 解决

代码

我们可以用 useCallback 来缓存这个函数引用:

js 复制代码
const handleClick = useCallback(() => {
  console.log("Clicked");
}, []);

完整代码如下:

js 复制代码
"use client";

import React, { useState, useCallback } from "react";

function Child({ onClick }: { onClick: () => void }) {
  console.log("🔄 Child render");
  return <button onClick={onClick}>点击子组件按钮</button>;
}

const MemoChild = React.memo(Child);

function App() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log("Clicked");
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <div>
        <button onClick={() => setCount(c => c + 1)}>加一</button>
      </div>
      <div>
        <MemoChild onClick={handleClick} />
      </div>
    </div>
  );
}

export default App;

分析

✅ 现在,当我们点击"加一"按钮时:

  • 父组件会重新渲染;
  • handleClick 的引用未变;
  • 子组件 MemoChild 不再重新渲染。

性能优化达成 ✅


🔍 四、useCallbackuseMemo 的区别

Hook 返回值 主要用途
useCallback(fn, deps) 返回函数 缓存回调函数引用
useMemo(factory, deps) 返回 缓存计算结果

其实:

js 复制代码
useCallback(fn, deps) ≡ useMemo(() => fn, deps)

但语义上更清晰:

  • 当你缓存的是函数 ,用 useCallback
  • 当你缓存的是计算结果 ,用 useMemo

🧠 五、使用时机与注意事项

✅ 适合使用的场景

1. 子组件使用了 React.memo,并且接收函数作为 props
2. 函数在依赖变化前后逻辑相同;
3. 函数的重新创建会导致不必要的渲染或副作用。

⚠️ 不建议滥用

* useCallback 本身也有性能开销;
* 对普通函数(非 props 传递、不影响渲染)没必要使用;
* 若滥用,会增加代码复杂度且收益有限。

🔧 建议

先写出正确的代码,再通过性能分析(如 React DevTools Profiler)决定是否引入 useCallback


💡 六、进阶技巧:搭配自定义 Hook 使用

在自定义 Hook 中使用 useCallback,可以有效避免无限循环:

js 复制代码
function useFetch(url) {
  const [data, setData] = useState(null);

  const fetchData = useCallback(async () => {
    const res = await fetch(url);
    const json = await res.json();
    setData(json);
  }, [url]);

  useEffect(() => {
    fetchData();
  }, [fetchData]);

  return data;
}

这里若不加 useCallbackfetchData 每次都会变,useEffect 就会无限执行。

在这个代码中,fetchData 会因为 url 变化而变化。具体来说:

  • useCallback 的依赖项是 [url],所以当 url 发生变化时,fetchData 函数会重新创建
  • useEffect 的依赖项是 [fetchData],所以当 fetchData 变化时,useEffect 会重新执行
  • 当 useEffect 重新执行时,它会调用新的 fetchData 函数,从而触发新的 API 请求

🧩 七、总结

项目 说明
作用 缓存函数引用,避免子组件重复渲染
语法 useCallback(fn, deps)
适用场景 函数作为 props 传递给子组件时
常见搭配 React.memouseEffect
不要滥用 若无性能瓶颈,使用纯函数更简单

🎯 结语

useCallback 并不是一个"必须用"的 Hook,而是在特定性能场景下的优化工具

理解它的核心思路------函数引用的稳定性,才能在正确的地方使用它。

相关推荐
有梦想的攻城狮9 小时前
通过Lettuce实现PB3格式对象在Redis中的存储与查询
数据库·redis·缓存·pb3
一个儒雅随和的男子10 小时前
多级缓存解决方案
spring boot·缓存
⑩-10 小时前
Redis(1)
数据库·redis·缓存
用户479492835691510 小时前
React DevTools 组件名乱码?揭秘从开发到生产的代码变形记
前端·react.js
开发者小天15 小时前
React中使用useParams
前端·javascript·react.js
ifeng091816 小时前
HarmonyOS资源加载进阶:惰性加载、预加载与缓存机制
深度学习·缓存·harmonyos
大隐隐于野16 小时前
从零开始理解和编写LLM中的KV缓存
java·缓存·llm
GISer_Jing19 小时前
跨端框架对决:React Native vs Flutter深度对比
flutter·react native·react.js
WYiQIU20 小时前
大厂前端岗重复率极高的场景面试原题解析
前端·javascript·vue.js·react.js·面试·状态模式
milanyangbo21 小时前
从同步耦合到异步解耦:消息中间件如何重塑系统间的通信范式?
java·数据库·后端·缓存·中间件·架构