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,而是在特定性能场景下的优化工具

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

相关推荐
摘星编程1 小时前
React Native + OpenHarmony:Timeline垂直时间轴
javascript·react native·react.js
AZ996ZA1 小时前
自学linux第十八天:【Linux运维实战】系统性能优化与安全加固精要
linux·运维·安全·性能优化
jin1233222 小时前
React Native鸿蒙跨平台完成剧本杀组队详情页面,可以复用桌游、团建、赛事等各类组队详情页开发
javascript·react native·react.js·ecmascript·harmonyos
he___H3 小时前
Redis高级数据类型
数据库·redis·缓存
jin1233223 小时前
基于React Native鸿蒙跨平台移动端表单类 CRUD 应用,涵盖地址列表展示、新增/编辑/删除/设为默认等核心操作
react native·react.js·ecmascript·harmonyos
徐同保4 小时前
React useRef 完全指南:在异步回调中访问最新的 props/state引言
前端·javascript·react.js
惊讶的猫4 小时前
Redis双写一致性
数据库·redis·缓存
浮游本尊4 小时前
React 18.x 学习计划 - 第十三天:部署与DevOps实践
学习·react.js·状态模式
摘星编程5 小时前
OpenHarmony环境下React Native:DatePicker日期选择器
react native·react.js·harmonyos
橙露5 小时前
NNG通信框架:现代分布式系统的通信解决方案与应用场景深度分析
运维·网络·tcp/ip·react.js·架构