React 中的 useMemo 与 useCallback:性能优化的利器

React 中的 useMemo 与 useCallback:性能优化的利器

在 React 开发中,随着组件复杂度提升,性能优化逐渐成为不可忽视的问题。当组件状态更新时,函数组件会重新执行,这可能导致不必要的计算或子组件重渲染。React 提供的useMemouseCallback两个 Hooks,正是解决这类性能问题的关键工具。本文将结合实际代码,详细讲解它们的作用、用法及应用场景。

为什么需要性能优化?

在 React 中,当组件的状态(useState)或上下文(useContext)发生变化时,组件会重新执行其函数体。这一机制确保了 UI 与数据的同步,但也可能带来副作用:

  • 组件内的计算逻辑会被重复执行(即使计算结果不变)
  • 传递给子组件的 props (尤其是函数)会被重新创建,导致子组件不必要的重渲染

例如,在未优化的情况下,当一个与计算逻辑无关的状态变化时,过滤列表或复杂计算会被重复执行;父组件传递的回调函数每次都会是新的引用,导致使用memo包装的子组件依然重渲染。 useMemouseCallback正是为解决这些问题而生。

useMemo:缓存计算结果

作用

useMemo用于缓存计算结果,避免组件重渲染时重复执行昂贵的计算逻辑。只有当依赖项发生变化时,才会重新计算结果。

用法

javascript 复制代码
const 缓存的结果 = useMemo(() => { 
    // 计算逻辑(可能是昂贵的操作) 
    return 计算结果; 
}, [依赖项数组]); // 依赖项变化时,重新执行计算
  • 第一个参数:一个函数,封装需要缓存结果的计算逻辑
  • 第二个参数:依赖项数组,只有数组中的值发生变化时,才会重新执行第一个函数

代码示例

  1. 过滤列表优化
ini 复制代码
const [count,setCount] = useState(0);
const list = ['apple','banana','orange','pear']; 
const [keyword,setKeyword] = useState('');
// 用useMemo缓存过滤结果,仅当keyword变化时重新过滤 
const filterList = useMemo(() => { 
    return list.filter(item => item.includes(keyword)) 
}, [keyword])
return (
    <input type="text" value={keyword} onChange={e => setKeyword(e.target.value)}/>
     {
        filterList.map(item => (
          <li key={item}>{item}</li>
        ))
      }
    <button onClick={() => setCount(count + 1)}>count + 1</button>
)

若不使用useMemo,每次组件重渲染(即使count变化,与过滤逻辑无关),filter都会重新执行。使用useMemo后,只有keyword变化时才会重新过滤,减少了不必要的计算。

  1. 昂贵计算优化
ini 复制代码
// 模拟一个昂贵的计算(循环100万次) 
function slowSum(n) { 
    console.log('计算中...'); 
    let sum = 0; 
    for(let i=0;i<n*1000000;i++){ 
        sum+=i; 
    } 
    return sum; 
} 
// 用useMemo缓存计算结果,仅当num变化时重新计算 
const result = useMemo(() => { 
    return slowSum(num); 
}, [num])

slowSum是一个耗时计算,若不缓存,每次组件重渲染(如countkeyword变化)都会触发它。使用useMemo后,只有num变化时才会重新执行,显著提升性能。

应用场景

  • 包含昂贵计算的逻辑(如大量循环、复杂数据处理)
  • 需要作为props传递的计算属性(避免子组件因值频繁变化而重渲染)
  • 依赖多个状态 / 变量的推导值(确保仅在依赖变化时更新)

useCallback:缓存回调函数

作用

useCallback用于缓存函数引用 ,避免组件重渲染时重复创建相同逻辑的函数。常与memo配合使用,防止子组件因接收的函数props变化而不必要地重渲染。

用法

scss 复制代码
const 缓存的函数 = useCallback(() => { 
    // 函数逻辑 
}, [依赖项数组]); // 依赖项变化时,重新创建函数
  • 第一个参数:需要缓存的回调函数
  • 第二个参数:依赖项数组,只有数组中的值发生变化时,才会重新创建函数

代码示例

  1. 子组件优化
javascript 复制代码
// 用memo包装子组件,使其仅在props真正变化时重渲染
const Child = memo(({count,handleClick}) => {
    console.log('child 重新渲染');
    return (
        <div onClick={handleClick}>
            子组件{count}
        </div>
    )
})
  1. 缓存回调函数
javascript 复制代码
// 用useCallback缓存handleClick,仅当count变化时重新创建 
const handleClick = useCallback(() => { 
    console.log('click'); 
}, [count])

若不使用useCallback,每次父组件重渲染,handleClick都会是新的函数引用。即使memo包装了Child,由于handleClick变化,子组件依然会重渲染。使用useCallback后,只有count变化时handleClick才更新,因此当num变化时,子组件不会重新渲染。

应用场景

  • 传递给子组件的回调函数 (如onClickonChange
  • 作为依赖项传入useEffect等 Hooks 的函数(避免useEffect不必要地触发)
  • 配合memouseMemo等优化手段,确保子组件仅在必要时重渲染

总结

useMemouseCallback都是 React 性能优化的核心工具,它们的本质是缓存

  • useMemo缓存计算结果,解决 "重复计算" 问题
  • useCallback缓存函数引用,解决 "子组件不必要重渲染" 问题

使用时需注意:

  • 不要过度优化:简单计算或不频繁重渲染的组件无需使用,缓存本身也有开销
  • 依赖项数组必须正确:遗漏依赖会导致缓存结果 / 函数过时,引发逻辑错误
  • 结合场景使用:昂贵计算用useMemo,回调函数用useCallback配合memo

合理使用这两个 Hooks,能有效提升 React 应用的性能,尤其在复杂组件或高频更新场景中效果显著。

相关推荐
用户47949283569153 分钟前
同事一个比喻,让我搞懂了Docker和k8s的核心概念
前端·后端
烛阴11 分钟前
C# 正则表达式(5):前瞻/后顾(Lookaround)——零宽断言做“条件校验”和“精确提取”
前端·正则表达式·c#
C_心欲无痕27 分钟前
浏览器缓存: IndexDB
前端·数据库·缓存·oracle
郑州光合科技余经理33 分钟前
技术架构:上门服务APP海外版源码部署
java·大数据·开发语言·前端·架构·uni-app·php
GIS之路42 分钟前
GDAL 实现数据属性查询
前端
PBitW2 小时前
2025,菜鸟的「Vibe Coding」时刻
前端·年终总结
mwq301232 小时前
不再混淆:导数 (Derivative) 与微分 (Differential) 的本质对决
前端
小二·3 小时前
Vue 3 组件通信全方案详解:Props/Emit、provide/inject、事件总线替代与组合式函数封装
前端·javascript·vue.js
研☆香3 小时前
html框架页面介绍及制作
前端·html
be or not to be3 小时前
CSS 定位机制与图标字体
前端·css