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 应用的性能,尤其在复杂组件或高频更新场景中效果显著。

相关推荐
前端之虎陈随易26 分钟前
编程语言级别的Skill市场,AI Agent 的未来形态
前端·vue.js·人工智能·typescript·node.js
一路向北he29 分钟前
字节钢铁军团--“提供情境,而非控制”
java·开发语言·前端
kyriewen1 小时前
豆包和千问同时关了智能体,我用它们搭的 3 个自动化全废了——迁移方案整理
前端·javascript·ai编程
前端一小卒1 小时前
我用 TypeScript 从零手写了一个 Claude Code,然后发现它的核心只有 30 行
前端·agent
大圣编程3 小时前
Python中continue语句的用法是什么?
开发语言·前端·python
yuhaiqiang3 小时前
随手 vibecoding 的浏览器插件已经 6000 多次下载,聊聊他的产品设计
前端·后端·面试
之歆3 小时前
Vue商品详情与放大镜组件
前端·javascript·vue.js
再吃一根胡萝卜4 小时前
如何把小米 MiMo 接入 CodeBuddy,打造私有 Agent
前端
负责的蛋挞5 小时前
异步HttpModule的实现方式
java·服务器·前端
YFF菲菲兔6 小时前
其他 Hooks 解析
react.js