当前位置: 首页 > news >正文

React学习———React.memo、useMemo和useCallback

React.memo

React.memo是React提供的一个高阶组件,用于优化函数组件的性能,它通过记忆组件的渲染结果,避免在父组件重新渲染时,子组件不必要的重新渲染
React.memo会对组件的props进行浅比较,如果props没有变化,则组件不会重新渲染

基本用法

import React from 'react'
const MyComponent = (props) => {console.log('组件渲染了')return <div>{props.value}</div>
}
export default React.memo(MyComponent)

工作原理

  • 默认浅比较:React.memo会对传递给组件的props进行浅比较,如果props没有变化,则跳过重新渲染
  • 自定义比较函数(可选):如果需要更复杂的比较逻辑,可以通过第二个参数传入自定义比较函数

自定义比较函数

const MyComponent = (props) => {console.log('组件渲染了')return <div>{props.value}</div>
}
const areEqual = (prevProps, nextProps) => {// 自定义比较逻辑return prevProps.value === nextProps.value
}
export default React.memo(MyComponent, areEqual)

使用场景

  • 纯函数组件:当组件的渲染结果完全依赖于props,且没有内部状态或副作用时,使用React.memo可以有效避免不必要的渲染
  • 频繁渲染的组件:在父组件频繁更新,但子组件的props变化较少的情况下,使用React.memo可以显著提升性能
  • 大型列表或复杂组件:对于渲染成本较高的组件,使用React.memo可以减少渲染次数,提升应用的整体性能

注意事项

  • 浅比较的局限性:React.memo默认使用浅比较。如果props是复杂的嵌套对象或数组,可能需要自定义比较函数
  • 状态或上下文变化:React.memo只对props的变化敏感,如果组件依赖于状态(state)或上下文(context),这些变化仍会触发重新渲染
  • 过度优化:不要滥用React.memo。因为浅比较和自定义比较函数本身也会带来一定的性能开销

useMemo

useMemo是一个React Hook,用于缓存计算结果,避免在每次渲染时重复执行耗时的计算

特点

  • 用于性能优化,减少不必要的计算
  • 只有依赖项发生变化时,才会重新计算值
  • 返回缓存的计算结果

基本用法

import React, { useMemo, useState } from 'react';
function ExpensiveCalculation(num){console.log('Calculating...');return num * 2
}
function App(){const [count,setCount] = useState(0)const [other, setOther] = useState(0);const result = useMemo(() => ExpensiveCalculation(count), [count])return (<div><p>Result: {result}</p><button onClick={() => setCount(count + 1)}>Increment Count</button><button onClick={() => setOther(other + 1)}>Increment Other</button></div>);
}
export default App
  • 第一个参数:回调函数
  • 第二个参数(依赖数组):当依赖项发生变化时,useMemo会重新执行回调函数并返回新的计算结果,如果依赖项没有变化,useMemo会直接返回之前缓存的结果,而不是重新执行回调函数

注意事项

  • useMemo只会缓存计算结果,不会缓存函数本身,如果需要缓存函数本身,使用useCallback
  • 如果依赖项数组为空([]),useMemo的值只会在组件首次渲染时计算一次

useCallback

useCallback是React提供的一个Hook,用于换成函数的引用,避免在组件重新渲染时不必要的重新创建函数

基本用法

const memozedCallback = useCallback(() => {// 函数逻辑},[依赖项]
)
  • 第一个参数:需要缓存的函数
  • 第二个参数(依赖数组):当依赖项发生变化时,useCallback会返回一个新的函数引用;如果依赖项没有变化,则返回缓存的函数引用

为什么需要useCallback

  • 在React中,函数组件每次渲染都会重新创建内部的函数,如果这些函数被传递给子组件或用作依赖项,可能会导致性能问题或不必要的副作用,
  • 例如:子组件的不必要重新渲染;性能浪费,尤其在函数被频繁创建时

使用场景

  • 避免子组件不必要的重新渲染
import React, { useState, useCallback, useEffect } from 'react';
// ({onClick}):表示从props中解构出onClick的属性
// ({onClick:() => void}):为onClick属性添加类型注解,表示他是一个函数,且没有参数,返回值为void
const Child = React.memo(({onClick}:{onClick:() => void})=>{console.log('子组件渲染')return <button onClick={onClick}>点击</button>
})function Parent(){const [count,setCount] = useState(0)const handleClick = useCallback(() => {console.log('按钮点击')}, [])return (<div><p>计数:{count}</p><button onClick={() => setCount(count+1)}>增加计数</button><Child onClick={handleClick} /></div>)
}

1:未使用useCallback时,每次Parent组件重新渲染时,handleClick都会被重新创建,导致Child组件也会重新渲染
2:使用useCallback后:handleClick的引用不会改变,Child组件不会重新渲染

  • 作为依赖项传递给useEffect
import React, { useState, useCallback, useEffect } from 'react';
function Example(){const [count,setCount] = useState(0)const logCount = useCallback(() => {console.log(`当前计数:${count}`);}, [count])useEffect(() => {logCount()}, [logCount]) // 使用 useCallback 缓存的函数作为依赖项return <button onClick={() => setCount(count + 1)}>增加计数</button>;
}

1:未使用useCallback时:logCount 每次渲染都会重新创建,导致useEffect每次都重新执行
2:使用useCallback后:logCount只有在count 变化时才会更新,减少不必要的副作用

useCallback和React.memo的区别

特性useCallbackReact.memo
作用对象用于优化函数用于优化组件
核心功能避免函数在组件重新渲染时被重新创建避免组件因props未变化而重新渲染
工作原理缓存函数引用,只有依赖项变化时才重新创建浅比较props,决定是否跳过组件渲染
适用场景当函数被传递给子组件或用作依赖项时当组件的props很少变化时
性能优化的目标减少函数的重新创建次数减少组件的重新渲染次数

useCallback和React.memo结合使用

在实际开发中,经常会被结合使用,尤其是在父组件向子组件传递函数时

useMemo和React.memo的区别

特性useMemoReact.memo
作用缓存计算结果优化组件渲染,避免不必要的重新渲染
触发条件比较依赖项是否变化比较组件的props是否变化
适用场景用于函数或计算逻辑的性能优化用于组件级别的性能优化
返回值返回缓存的计算结果返回优化后的组件

总结

  • React.memo用于组件级别的性能优化,避免子组件不必要的重新渲染
  • useMemo用于缓存计算结果,适合优化耗时的计算逻辑
  • useCallback用于缓存函数引用,避免优化子组件接收函数props的场景
http://www.xdnf.cn/news/454393.html

相关文章:

  • LeetCode100.7 接雨水
  • 【python爬虫】python+selenium实现Google Play Store应用信息爬虫+apk下载
  • 内存泄漏系列专题分析之十四:高通相机CamX ION/dmabuf内存管理机制ImageBuffer之GrallocBuffer原理
  • 代码随想录算法训练营Day58
  • 01-three.js vite基础示例
  • 机器视觉助力轨道缺陷检测
  • Python常用魔术方法
  • 分布式2(限流算法、分布式一致性算法、Zookeeper )
  • 解密企业级大模型智能体Agentic AI 关键技术:MCP、A2A、Reasoning LLMs-强化学习算法AlphaGo
  • sqlalchemy库详细使用
  • 【C++】17. 多态
  • AI智能体应用平台-智能体定制-企业级agent开发平台哪个更好?
  • 【嵌入式开发-按键扫描】
  • 从构想到交付:专业级软开发流程详解
  • c++中的函数(默认参数,占位参数,重载)
  • Arduino使用红外收发模块
  • MySQL基础之开窗函数
  • 嵌入式(c语言篇)Day9
  • 基于nacos2.5.1的java微服务项目开发环境配置简介
  • Spyglass:跨时钟域同步(同步单元)
  • Gin 框架指南(代码+通俗解析版)
  • 2025前四月新能源汽车出口增长52.6%,外贸ERP数字化助力汽企持续跃升
  • 给 DBGridEh 增加勾选用的检查框 CheckBox
  • 通用软件项目技术报告 - 导读I
  • C++ 并发编程(1)再学习,为什么子线程不调用join方法或者detach方法,程序会崩溃? 仿函数的线程启动问题?为什么线程参数默认传参方式是值拷贝?
  • 阿里的库存秒杀实现与Inventory Hint技术解析
  • Windows系统Anaconda/Miniconda的安装、配置、基础使用、清理缓存空间和Pycharm/VSCode配置指南
  • Linux系统编程——fork函数的使用方法
  • idea插件使用
  • Prometheus 的介绍与部署(入门)