React 【一】Memo是用来干什么的

前言

React.Memo 是 React 16.6 新的一个 API, 用来缓存组件的渲染,避免不必要的更新,也是一个高阶组件,与类组件的 PureComponent十分类似,不同点在于,React.Memo 只能用于函数组件。

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

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

造成无效渲染的原因:

函数组件本身没有识别prop值的能力,每次父组件更新的时候都相当于是给子组件一个新的prop值。

很明显,对于没有用到被改变的那个状态的组件来说,重新渲染完全没有必要。此时就要考虑到性能优化,避免该组件重新渲染。所以,react.Memo 就此诞生。

memo是一种缓存技术,这个函数可以检测从父组件接收的props,并且在父组件改变state的时候对比这个state是否是本组件在使用,如果不是,则拒绝重新渲染。


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

举个例子

// 这个是父组件
import React, { useState } from 'react';
import Child from '@/pages/bidSectionApply/bidSectionApply/components/Child';

const Father = () => {
  const [list, setList] = useState([1,2,3,4,5])
  const [childData, setChildData] = useState(1) // 传入子组件的状态
  const [btnData, setBtnData] = useState(2) // 没有传入子组件的状态

  return (
    <div style={{height: '100%'}}>
      <p>我是父组件</p>
      <button onClick={() => setBtnData((val: any) => val + 1)}>改变btnData的值,btnData是没有传入子组件的</button>
      <Child list={list} childData={childData}></Child>
    </div>
  )
}

export default Father
// 这个是子组件
import React from 'react';

const Child = (props: any) => {
  const { list, childData } = props
  return (
    <>
      <h1>我是子组件,下面是父组件传过来的数据</h1>
      {console.log('组件渲染了!!!')}
      <div>{childData}</div>
    </>
  )
}

export default Child

在上面的父组件代码中,定义了两个状态,一个传入了子组件(childData),一个没有传入子组件(btnData)

问:当点击父组件的按钮,改变 btnData 的时候,没有改变传入子组件的数据的状态,子组件会被重新渲染吗?

答:会重新渲染。只要父组件的状态改变,所有的子组件不论是否使用到了被改变的那个state都会被重新渲染。如下图所示:

在这里插入图片描述
按钮多次点击,子组件的被重新渲染了多次。显然这种渲染是没有必要的,于是在阻止重新渲染这个需求的基础上,诞生了memo()memo是一种缓存技术,这个函数可以检测从父组件接收的props,并且在父组件改变state的时候对比这个state是否是本组件在使用,如果不是,则拒绝重新渲染。

memo使用方式

memo的使用方式可谓是十分简单了,只需要在导出的时候把子组件当初这个函数的入参包起来就好了。

// 使用 memo 后的代码
import React, { memo } from 'react';

const Child = (props: any) => {
  const { list, childData } = props
  return (
    <>
      <h1>我是子组件,下面是父组件传过来的数据</h1>
      {console.log('组件渲染了!!!')}
      <div>{childData}</div>
    </>
  )
}

export default memo(Child)

这样,只要传入子组件的状态不变,无论点击按钮多少次,子组件也不会重新渲染,节省了性能。

总结:
如果当前组件被memo保护,那么当前组件的props不变,组件不会进行重新渲染 。这样,我们在项目内合理的使用memo会带来很大的性能提升

注意

1、 有一种情况,被memo保护的组件,即使 props 变了,也不会重新渲染。

即:当被改变的那个props是一个数组(对象)的时候,具体情形如下描述:

还用上图的例子,传入了一个 list 数组,子组件内部是被memo缓存了的。如果我们往 list 这个数组中使用 push()方法,push进去几个数据,子组件的props改变了,理论上说,子组件应该会被重新渲染,但实际上并不会。

这是因为memo的保护,是对props做一个浅比较

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

解决方法

需要让memo检测到数组栈地址的变化即可,即返回一个全新的数据就行。

所以,我们可以使用拓展与算符的形式。代码如下所示:

	const [list,setList] = useState([1,2,3,4,5]);
	
	setList(list.push(1)); //这样是不会被memo检测到的,是无法触发memo更新的
	
	setList([...list,1]); //这样才可以,创建一个新数组,再在里面解构旧数组,往后面追加 1
	//这样,就等于返回了一个新的数组,栈中的地址就会改变,memo就可以检测到并触发更新

这样,既可以修改数组,又触发组件更新

2、有人会问:既然memo这么好用,是不是要把项目内的每一个组件都包裹一下。

注意,注意:不能把memo给全包。试想一下,如果这样效果很好,为什么react没有直接把memo设置成默认的呢。

上面说过,memo是一种缓存技术,缓存也需要成本。如果每个组件都进行缓存,会给浏览器带来非常非常大的负担。

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

好了,以上就是关于memo的详细介绍。当然,memo的内容肯定不止这一点,比如memo如何进行深比较,memo的功能是如何实现的,memo配合useMemo、useCallback使用、memo的第二个传参等等,这些内容会在接下来的文章中进行更新。

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值