这里提一下,如果大家看到这个标题有所疑惑的话,可以花点时间看一下本篇文章。反之呢如果是看到标题第一时间就反映出结论的话,就可以去get其他文章的知识点了

那么接下来就不废话了,直接长刀直入,进入正题!

初探memo

首先让我们用一个例子走进React.memo的世界

呆呆的函数组件 - 没有使用memo

对于一个函数组件来说,如果没有使用React.memo就好比是一个人没有脑子,就笨笨的呆呆的

不信我们就来看下面的Demo

点击访问演示Demo

让我们来分析下上图发生的流程:

  1. 页面第一次加载,渲染App组件和B组件,控制台打印效果如上图
  2. 点击按钮,改变App组件内的值。App组件和B组件全都发生更新

那么问题就来了,按正常逻辑来说,应该是这样的流程才对:

  1. 页面第一次渲染,App组件和B组件分别更新,并打印
  2. App组件内的数据发生变化所以,App组件重新渲染
  3. 改变的数据和B组件毛关系没有,B组件维持原状,不进行更新渲染

but理想异常丰满的,现实十分骨干的。 事实就是不但App组件发生了更新,B组件也跟着进行了更新,这不是我们想要的,因为对于B组件来说:明明老子啥都没干,却还非要我再重新穿一遍衣服?

无效渲染的原因

那么造成无效渲染的原因是啥呢?

其实简单说来是这样的:

函数组件本身没有识别prop值的能力,每次父组件更新的时候都相当于是给子组件一个新的prop值。所以就相当于B组件这小子因为没带脑子(React.memo),是个呆呆的二傻子,所以他做为一个普通组件,就没有分别prop的能力,当他看到别人都更新了也就跟着把自己也造了一遍,因此就会造成上面中的问题。

给憨憨带上脑子 - 使用memo进行包裹

给函数组件带上脑子 当我们给一个函数组件带上脑子的时候,就想下面这样

import React form 'react';const FuncComponent = ()=>{return <h1>火热很火辣</h1>
}export default React.memo(FunComponent);

就不会发生上面那种,无脑render组件的情况了

试着把上面demo中B组件代码里最后一行的注释放开试一下吧

然后像上面一样再次点击一下按钮,看看控制台的打印结果:

Yep! 只更新了App组件,符合预期!

那么到底是为什么造成的这种原因呢

所以到这里还不算完,让我们进一步升温

激情升温 - 深入探索

到这里其实我们还是不太清楚memo是怎么做到避免无效更新的,接下来我们就来扒一扒!

class组件中的性能优化点

不知道大家有没有发现class组件中也有一个这样作用的东西,叫做PureComponent,它的功能和memo是一毛一样的。

来回顾一下,我们在class组件中经常用到的写法:

import React, {PureComponent} from 'react';class Demo extends PureComponent {// 性能优化点shouldComponentUpdate(nextProps, nextState){  // 默认始终返回truereturn true;}render() {return <h1>听懂掌声</h1>}}

总的来说其实PureComponnet和memo都是通过对props值的浅比较来决定该组件是否需要更新的。

如果我们在class组件中,不主动使用PureComponent,也可以手动的去决定该组件是否更新,具体做法:

在生命周期shouldComponentUpdate,来通过对当前porps以及state值的对比,然后返回一个布尔值(true或者false)来决定该组件是否更新。

其实PureComponent组件就是把这对比值的部分功能帮我们完成了,方便我们直接使用,而不用再去手动的去写代码进行类似的优化。

memo的功能实现

这里是我的猜想哈,memo的原理和PureComponent应该是一样的,从开发者的角度去想,既然class组件有这样一个优化方法,那既然要推行Hook,函数组件也必定需要一个类似功能的方法去帮助大家减少代码优化的工作量。所以感觉两者在功能的实现上应该大部分都是一致的。 这里也放上一段React中PureComponent进行浅比较的代码,方便大家进一步理解

function shallowEqual (objA: mixed, objB: mixed): boolean {//  这里的is是判断两个值是否相等,只不过是对 + 0 和 - 0,以及 NaN 和 NaN 的情况进行了特殊的处理封装,目前react源码中好像有一套新的is判断if (is (objA, objB)) {return true;}// 判断是否为对象类型if (typeof objA !== 'object' ||objA === null ||typeof objB !== 'object' ||objB === null) {return false;}// 比较两个对象的,属性数量是否相等const keysA = Object.keys (objA);const keysB = Object.keys (objB);if (keysA.length !== keysB.length) {return false;}// 比较两个对象的的属性是否相等,值是否相等for (let i = 0; i < keysA.length; i++) {if (!hasOwnProperty.call (objB, keysA [i]) ||!is (objA [keysA [i]], objB [keysA [i]])) {return false;}}return true;
}

这就是react中进行浅层比较的源码,也是PureComponent和memo决定是否更新组件的重要依据。

memo配合useMemo、useCallback

一般在项目的优化实践中,memo包裹的函数组件都是要配合useMemo和useCallback来使用的

对于useMemo和useCallback其实我不准备长篇大幅的讲述了,因为社区已经有很多不错的文章了,大家可以搜来看一下。 我这里只做一个说人话的简单介绍就好了

useCallback

  const memoizedCallback = useCallback(() => {doSomething(a, b);},[a, b],);

返回值是一个函数(memoizedCallback),这个函数就是作为第一个参数传进去的那个。区别就是作为返回值的这个函数是一个memoized的版本,用人话理解就是:保持了函数的引用。不会在组件更新时,去重新声明函数,从而改变在内存中的引用地址。

除非是第二个参数数组里的依赖项发生改变,否则这个做为返回值的函数(memoizedCallback)就一直保持原先的状态

应用场景

经常使用在父组件A向子组件B传递一个函数作为prop值的时候

父组件A:

import React,{ useCallback } form 'react';const A = () => {return (// 如果不使用useCallback包裹的话,每次A的更新,都会重新声明这个handleClick的这个函数,导致B组件无效的更新<B handleClick={ useCallback( () => //doSomething,[x,xx]) };);
}export default A;

子组件B:

import React,{ memo } form 'react';const B = (props) => {const { handleClick } = props;return <div onClick={ handleClick }>卑微小B在线被Diss</div>;
}
// 这里需要注意,要配合memo使用,否则的话不带脑子的B组件会始终认为传递过来的prop值都是一个全新的
export default memo(B);

useMemo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

其实和useCallback很像,只不过是useMemo返回的是一个,而不是一个函数

useCallback 的第一个参数是函数,这个函数的返回值会作为useMemo的返回值memoizedValue)

除非是第二个参数数组里的依赖项发生改变,否则这个做为返回值(memoizedValue)就一直保持原先的值

useCallback能做的事useMemo都能做,但是还是推荐各司其职

const fn = useCallback( () => //doSomething , [x,xx])
// 相当于
const fn = useMemo( ()=> () => //doSomething , [x,xx])
// 因为useMemo的返回值是第一个函数的返回值,所以只要让第一个参数的函数返回一个函数就可以达到useCallback的效果

结尾 需要这些资料,可以 点击这里 领取

为什么要在函数组件中使用React.memo?相关推荐

  1. react组件放在数组中_为什么要在函数组件中使用React.memo?

    这里提一下,如果大家看到这个标题有所疑惑的话,可以花点时间看一下本篇文章.反之呢如果是看到标题第一时间就反映出结论的话,就可以去get其他文章的知识点了 那么接下来就不废话了,直接长刀直入,进入正题! ...

  2. antd 函数组件_react函数组件中引用antd<Form/>组件demo

    ```xml import React, { forwardRef, useEffect } from 'react'; import { Input, Select, Row, Col, Butto ...

  3. react16:函数组件中使用props

    函数组件中也可以使用props.看个简单的例子,如下. <!DOCTYPE html> <html lang="en"> <head><m ...

  4. React 函数组件中无法清除定时器问题

    问题: 在离开当前组件页面后,清除定时器,定时器的标识会在一瞬间变为 undefined , 但立马就会又重新开始执行定时器,而且定时器标识不变. 原因: 函数组件的本质是一个函数,而在在一个局部函数 ...

  5. React 中ref 的使用(类组件和函数组件)以及forwardRef 与 useImperativeHandle 详解

    前言 在一个父组件中,我们想要获取到其中的节点元素或者子组件实例,从而直接调用其上面的方法.Class 类组件和函数组件是两种不同的写法. 1. Class 组件中使用ref 在 React 的 Cl ...

  6. onclick=两个函数_[译]React函数组件和类组件的差异

    [译]React函数组件和类组件的差异 原文: https://overreacted.io/how-are-function-components-different-from-classes/ 在 ...

  7. react render相关 【类组件、函数组件 】

    [类组件] 大概就是如果是class的写法,并且extends React.component 就要手动写render [函数组件] 如果是const App = ()=> {} 那么就酸函数组 ...

  8. react基础入门,类组件和函数组件,state,props,refs

    React入门 目录 React入门 React入门 Vue跟React的异同点 相同点 不同点 Vue小建议 1. 不需要响应式的数据应该怎么处理? 2. Key 3. 数据结构 React 教程 ...

  9. react中类组件传值,函数组件传值:父子组件传值、非父子组件传值

    父子组件传值.非父子组件传值: 类组件传值 父子 组件传值 子 传 父: 子组件:事件的触发sendMsg=()=>{this.props.person();}父组件:<Child per ...

最新文章

  1. 用Php设置Iptables,如何使用CSF添加自定义iptables规则
  2. 5类6类7类网线对比_未来值得投资的6类苗木,5年内市场前景应该都不错!
  3. kafka-0.10.2.1:Producer生产时无法自动创建Topic
  4. ASP技巧实例:几行代码解决防止表单重复提交
  5. redhat和ubuntu系统下挂载ntfs文件系统的方法(转载)
  6. DevC++的一些使用技巧
  7. Jenkins整合Sonar
  8. 数字格式化类NumberFormat
  9. 如何把linux 安装到u盘,利用U盘装CentOS 6.4和将CentOS 6.4安装到U盘
  10. Windows电脑电源选项开启“卓越性能”模式
  11. 无法远程连接到计算机 凭证,远程连接凭据不工作如何处理_远程桌面连接提示凭据无法工作的解决教程-win7之家...
  12. linux和windows文件加密,在Linux和 Windows 上使用 EncFS,如何加密雲存儲
  13. 编译原理-LL(1)语法分析器
  14. Unirech阿里云国际版云服务器代充-使用Python批量创建实例
  15. 原创超简单代码(1.21.50)
  16. FLV.js播放报错,及浏览器播放flv缓存内存不足导致黑屏
  17. jdk的安装、卸载与简介
  18. 2021全球与中国智能音频SoC芯片市场现状及未来发展趋势
  19. 如何用SPSS进行数据分析?
  20. 微信小程序新蓝海全行业深度解析报告

热门文章

  1. Booktrack Classroom(有声电子书)
  2. 多层感知机(MultiLayer Perceptron)以及反向传播算法(Backpropagation)
  3. 【雪野实训记录】Oracle宾馆管理系统-L2综合项目案例
  4. [易飞]批量导出易飞ERP系统目前所有BOM(含本币未税单位成本)
  5. 300兆的网速测试软件,光纤是300兆的网速,用电脑测试只有100兆,光猫坏了?
  6. MySQL登录时出现 Access denied for user 'root'@'xxx.xxx.xxx.xxx' (using password: YES) 的原因及解决办法
  7. php反调试,简单对抗某个驱动的反调试
  8. 损失函数与准确率的关系
  9. Ruby on Rails 之旅(六)——Ruby on Rails 简介 ( 少就是多)
  10. linux 安卓模拟器破解版下载,靠谱助手安卓模拟器最新版