写点什么

React 中 useCallback 的基本使用和原理解析

作者:老李说技术
  • 2025-11-04
    陕西
  • 本文字数:1639 字

    阅读完需:约 5 分钟

React 中 useCallback 的基本使用和原理解析

React 中 useCallback 的基本使用方法

useCallback 是 React 的一个核心 Hook,用于​缓存函数定义​,避免组件重新渲染时重复创建函数实例。以下是其基本使用方法:

1. 基本语法

const memoizedCallback = useCallback(  () => {    // 函数逻辑 (例如更新状态、调用API等)    doSomething(a, b);  },  [a, b] // 依赖项数组);
复制代码


  • 第一个参数​:需要缓存的函数。

  • 第二个参数​:依赖项数组(Dependency Array),当数组中的变量变化时,函数会重新创建。

2. 核心作用

  • 避免不必要的函数重建​:默认情况下,组件每次渲染都会创建新的函数实例,使用 useCallback 后可复用函数。

  • 优化子组件渲染​:当缓存的函数作为 props 传递给子组件(配合 React.memo)时,可避免子组件不必要的重渲染​。

3. 使用示例

import React, { useState, useCallback } from 'react';
function Counter() { const [count, setCount] = useState(0);
// 缓存函数:依赖项为空数组,函数只创建一次 const increment = useCallback(() => { setCount(prev => prev + 1); // 使用函数式更新避免闭包问题 }, []);
return ( Count: {count} +1 );}
复制代码


  • 依赖项 [] 表示函数仅在组件初次渲染时创建。

  • 使用 setCount(prev => prev + 1) 替代 setCount(count + 1) 可避免闭包陷阱(函数捕获过时状态)​。

4. 适用场景

useCallback,本质上是用于缓存函数。


如果函数,是以 props 的方式,传递给子组件,为了每次避免子组件的渲染,建议使用 useCallback 进行包裹。


但是每一次,使用 useCallback,我们考虑的首要问题是,这样真的优化了组件的性能吗?其实大多数场景,如果不是类似列表渲染的场景,这样不一定会优化了性能。


也就是,函数作为 props 传递给性能敏感的子组件的场景,才是使用 useCallback 的时候。

useCallback 的原理解析

  • useCallback 的主要目的是在依赖项不变的情况下,返回同一个函数引用,避免函数重复创建,从而优化性能。

  • useCallback它会在首次渲染时(或依赖项变化时)创建一个新的函数,并将其缓存起来。在后续渲染中,如果依赖项没有变化,则返回缓存的函数;否则,就重新创建函数并更新缓存。

  • 简易的伪代码,可能如下所示


let lastDeps; // 上一次的依赖项let lastCallback; // 上一次缓存的函数
function useCallback(callback, deps) { if (lastDeps === undefined) { // 第一次调用 lastDeps = deps; lastCallback = callback; return callback; }
// 检查依赖项是否变化 const hasChanged = deps.some((dep, index) => dep !== lastDeps[index]); if (hasChanged) { lastDeps = deps; lastCallback = callback; } return lastCallback;}
复制代码


每次掉用 useCallback,返回的函数,取决于依赖项有没有发生变化。


React 内部是咋样的呢?

1、Fiber 节点存储机制

React 在 Fiber 节点(组件实例对应的数据结构)中维护一个 memorizedState 链表,专门存储 Hooks 状态。


function updateCallback(callback, deps) {  const hook = updateWorkInProgressHook(); // 获取当前 Hook 节点  const nextDeps = deps === undefined ? null : deps;  const prevState = hook.memoizedState;     // 读取缓存的上次状态    // 依赖项对比:使用浅比较(shallow equal)  if (prevState !== null && areHookInputsEqual(nextDeps, prevState[1])) {    return prevState[0]; // 返回缓存的函数  }    //  依赖变化:缓存新函数  hook.memoizedState = [callback, nextDeps];  return callback;}
复制代码

2、依赖项对比算法

源码中的 areHookInputsEqual 对依赖数组进行浅比较(类似 Object.is):


function areHookInputsEqual(nextDeps, prevDeps) {  if (prevDeps === null) return false;  for (let i = 0; i < prevDeps.length; i++) {    if (!Object.is(nextDeps[i], prevDeps[i])) {      return false;    }  }  return true;}
复制代码


这种优化避免了深度比较的性能损耗

发布于: 刚刚阅读数: 5
用户头像

相信自己,能在技术的道路上走的更远! 2025-10-25 加入

前阿里技术专家,现大前端技术负责人,专注大前端领域知识分享

评论

发布
暂无评论
React 中 useCallback 的基本使用和原理解析_useCallback_老李说技术_InfoQ写作社区