浅谈React.memo函数

news/2024/11/30 11:42:52/

背景

在React中,组件渲染的是最常有的事情。但是,有部分的渲染是不必要的,是可以避免的。

在react的一般规则中,只有父组件的某一个状态改变,父组件下面所有的子组件不论是否使用了该状态,都会进行重新渲染。

显然,对于没有用到被改变的那个状态的组件来说,重新渲染是完全没有必要的。所以,React.memo就诞生了。

什么是 React.memo

React.memo 是一个高阶组件,它可以用来包装一个函数组件(Functional Component)并返回一个新的组件。这个新组件会对传入的 props 进行浅比较(Shallow Comparison),如果发现传入的 props 没有发生变化,则直接返回上一次渲染的结果,从而避免不必要的重复渲染过程,提升了组件的渲染性能。否则重新渲染组件。

React.memo 和 React.PureComponent 类似,都可以用于优化组件的性能。但是它们之间也存在着很大的区别,React.PureComponent 只适用于 class 组件,而 React.memo 则适用于所有函数组件。

React特性:父组件中状态的改变会让所有的子组件重新渲染

比如:

import React, { useMemo, useState } from "react";export const EgOfMemo: React.FC = () => {// 传入子组件的状态const [name, setName] = useState('lvxiaobu')// 没有传入子组件的状态,但是当age更新后,父组件会重新渲染,所有的子组件无论是否用到age,也全部会重新渲染const [age, setAge] = useState(0)return (<div>{/* 点击按钮,子组件Child会重新渲染 */}<button onClick={() => setAge(1)}> 点击修改年龄</button><Child name={name} /></div>)
}

只要父组件的状态改变,所有的子组件不论是否使用到了被改变的那个state都会被重新渲染。
显然这种渲染是完全没必要的。我又没有使用被改变的那个state,我本身本身也没有什么视图需要更新,根本没必要重新渲染。

在阻止重新渲染这个需求的基础上,诞生了memo函数,memo是react的一种缓存技术,这个函数可以检测从父组件接收的props,并且在父组件改变state的时候比对这个state是否是本组件在使用,如果不是,则不会重新渲染。

用法

使用 React.memo 很简单,只需要将要优化的函数组件作为参数传递给 React.memo 即可,例如:

import React, { memo } from 'react';function Child(props) {// 子组件的具体实现逻辑
}export default memo(Child);

在上面的例子中,我们使用 memo 包装了一个名为Child的函数组件,并导出了一个新的组件。此时,这个新组件会对传入的 props 进行浅比较,以便在 props 没有发生变化时直接使用上一次渲染的结果。
像这样,这Child就是被缓存成功了。下次当父组件中无关它的state(状态)被更新时候,Child组件就不会重新渲染了。
总之就是,如果当前组件被memo保护,那么当前组件的props不变,则组件不进行重新渲染。这样,我们合理的使用memo就可以为我们的项目带来很大的性能优化。

注意事项

虽然 React.memo 可以帮助我们优化组件的性能和渲染速度,但也存在着一些需要注意的事项。

1、首先,由于 React.memo 只进行浅比较,因此如果 props 中包含了引用类型的数据(如数组或对象),那么即使这些引用类型的数据没有发生变化,也会触发重新渲染。因此,需要特别注意在 props 中传递引用类型的数据时,要确保它们不会频繁地发生变化,才能获得最佳的性能表现。

import React, { useState } from "react";
import { EgOfMemo } from '../../components'export const Home: React.FC = () => {const [name, setName] = useState('');const [list, setList] = useState([1,2,3])const modifyName = () => {setName('lvxiaobu')list.push(4)// list更新了,但子组件没有重新渲染console.log(1, list)}return (<><div>{name}</div><button onClick={modifyName}>点击</button><EgOfMemo list={list}  /></>)
}

上面传入了一个list数组进去子组件,子组件内部是被memo缓存了的。点击按钮的时候,如果我们往list这个数组中push一个4,那么子组件中的props改变了,理论上来说,子组件应该重新渲染了。但实际上并不会。

这是为什么呢?因为memo的保护是对props做一个浅比较(只比较栈,不比较堆)

而数组的使用push()方法看似是变了。但变的只是堆中的数据,存在与栈中的地址依然不会改变。memo是检测不到的。所以,使用push等不能返回一个新数组的方法,均无法触发memo的更新机制。

想要改变数组也能让子组件更新,有方法。
就像刚刚说的,需要让memo检测到数组栈地址的变化。要栈地址变化的话,只要返回一个全新的数组就好了,比如:

setList([...list, 4]); //这样才可以,创建一个新数组,再在里面解构旧数组,往后面追加 1

这样,就可以既修改数组,又触发组件更新了。
2、一旦传入给子组件的是函数类型,那么子组件使用memo包裹也没法让它避免没有意义的渲染

import React, { useState } from "react";
import { EgOfMemo } from '../../components'export const Home: React.FC = () => {let [name, setName] = useState('1');const [list, setList] = useState([1,2,3])const modifyName = () => {setName(name + '!')}// 父组件状态更新就会重新渲染,每次重新渲染foo函数都会被重新构建,并返回新的内存地址给foo,所以即使子组件用了memo包裹,子组件依然认为props的内存地址更新了,故子组件会重新渲染const foo = () => {console.log('foo')}return (<><div>{name}</div><button onClick={modifyName}> 点击修改名字 </button><EgOfMemo list={list} foo={foo} /></>)
}

上面代码解读一下:父组件状态更新就会重新渲染,每次重新渲染foo函数都会被重新构建,并返回新的内存地址给foo,所以即使子组件用了memo包裹,子组件依然认为props的内存地址更新了,故子组件会重新渲染。
也就是说,一旦传入给子组件的是函数类型,那么子组件使用memo包裹也没法让它避免没有意义的渲染,那应该怎么做呢?使用useCallback函数配合,下节博客会介绍!

3、不能每个组件都用memo包裹:如果真的每个组件都有被缓存的必要而且不会给项目带来破坏性问题,为什么react不直接把memo设为默认的呢。当然是因为每个都缓存的话,会给项目带来毁灭性的问题咯。

我们需要知道的是,缓存也需要成本。如果每个组件都进行缓存,会给浏览器带来非常非常大的负担。

所以在平常项目中,我们需要挑选一些经常被使用,经常会被重新渲染的组件去有目标的缓存他。而不是每一个组件都缓存一下。切记切记。

在某些情况下,使用 React.memo 可能会导致性能反而变差,例如当组件的渲染开销很小、props 变化频繁或者组件本身就是高阶组件时。 因此,在使用 React.memo 时,需要根据具体的场景进行评估和调整,以获得最优的性能表现。

最后,需要注意的是,React.memo 并不是万能的优化工具,它只能针对某些特定场景进行优化,而对于一些更复杂的性能问题,还需要使用其他更加专业的工具和技术进行优化。

总结

  1. 父组件中state(状态)改变,不受memo保护的子组件也会重新渲染
  2. 被memo函数包起来的组件只有本身的props被改变之后才会重新渲染
  3. memo只能进行浅比较来校验决定是否触发重新渲染。所以改变数组(对象)类型的props的时候记得返回一个全新的数组(对象)
  4. memo不是项目中所有的组件都需要包一下。包的太多反而会起反效果,我们需要选择那些经常被重新渲染的组件有选择性的去缓存。

React.memo 是 React 中一个很实用的工具,可以帮助我们优化组件的性能和渲染速度。它通过记忆组件的 props,并在下一次渲染时只有当 props 发生变化时才会重新渲染组件,从而大大提高了组件的渲染性能!


http://www.ppmy.cn/news/156690.html

相关文章

acc 蓝牙_蓝牙耳机acc什么意思

大家好&#xff0c;我是时间财富网智能客服时间君&#xff0c;上述问题将由我为大家进行解答。 蓝牙耳机acc是杜比实验室为音乐社区提供的技术&#xff0c;是一种高压缩比的编码算法。 蓝牙耳机就是将蓝牙技术应用在免持耳机上&#xff0c;让使用者可以免除恼人电线的牵绊&…

蓝牙耳机连接macbook无声音

现象&#xff1a;蓝牙耳机连接Macbook&#xff0c;耳机没有声音。注意:是可以连接成功&#xff0c;但是声音仍是通过mac的外放输出的。 解决方案&#xff1a; 拔掉所有mac上的外设&#xff0c;无论是u盘还是雷电口。然后重启。尝试是否可以正常。 如果上面的方法不行。请关机&a…

E450连接蓝牙耳机

**1.正常情况下&#xff0c;右下角托盘有蓝牙的图标**2.如果没有这个蓝牙图标&#xff0c;那就要安装蓝牙驱动&#xff0c;驱动下载地址 https://think.lenovo.com.cn/support/driver/newdriversdownlist.aspx?ytpt&categoryid11942&CODENameThinkPadE450 安装 Intel …

电脑蓝牙耳机,蓝牙耳机能连笔记本吗_笔记本电脑怎么接蓝牙耳机吗-win7之家

近日有笔记本电脑用户想要连接蓝牙耳机来使用&#xff0c;可是却不知道蓝牙耳机能连笔记本吗&#xff1f;现在的很多笔记本电脑都自带蓝牙功能&#xff0c;所以无需额外硬件就可连接蓝牙耳机&#xff0c;如果你电脑中有自带蓝牙模块的话是可以连接的&#xff0c;下面就教大家笔…

蓝牙怎么调声音大小_苹果蓝牙耳机

摘要 腾兴网为您分享:湖北教育云app&#xff0c;贝才&#xff0c;网易云音乐格式转换器&#xff0c;买菜么&#xff0c;010editor&#xff0c;壹化网&#xff0c;延边百姓信息,联合平台&#xff0c;女生标准身高体重表图&#xff0c;党员责任岗&#xff0c;智慧大学&#xff0c…

蓝牙耳机降噪知识

蓝牙耳机上的降噪&#xff0c;根据原理可以分为以下几种。 1、被动降噪。 这个是指入耳式耳塞&#xff0c;由于耳塞是通过一个硅胶套塞入人耳的耳道的。所以有较好的隔离外界声音的作用。而且这个降噪是全频的&#xff0c;就是对从20赫兹到20K赫兹的声音都有效&#xff0c;而且…

蓝牙耳机最优秀的保养方法,别再担心你的蓝牙耳机寿命短了~

随着数码产品的不断更新换代&#xff0c;人们对于蓝牙耳机的需求也在不断的变化。首先就是对于耳机外观颜值的要求很高&#xff0c;其次就是保证音质的同时续航还要持久。市面上无线蓝牙耳机的款式也是层出不穷&#xff0c;很多人买到喜欢的蓝牙耳机最开始都是满心欢喜&#xf…

蓝牙怎么调声音大小_蓝牙耳机怎么调音量?

随着蓝牙无线技术和手机逐渐取消耳机接口的趋势,蓝牙耳机的使用越来越普遍,但是对于第一次用蓝牙耳机的网友来说头疼的问题是,蓝牙耳机怎么调音量,不同的蓝牙耳机又不同的设计,下面就为大家介绍几种常见的调节方式,希望能够帮助到您。 1、有的蓝牙耳机的耳机上拥有音量大…