在每个地方都应该添加 memo 吗?

在这里插入图片描述

文章概叙

本文主要讲的是React中memo的使用,以及考虑是否使用memo的判断依据

memo介绍

memo 允许你的组件在 props 没有改变的情况下跳过重新渲染。

在使用memo将组件包装起来之后,我们可以‍获得该组件的一个 记忆化 版本。通常情况下,只要该组件的 props 没有改变,这个记忆化版本就不会在其父组件重新渲染时重新渲染。

也正是因为如此,在开发过程中,我们都会说“用memo来缓存组件,跳过组件的重复渲染”等说法,此时所说的memo就是该API,当我们传入的props没有变化(需要注意的是,数组等对象,引用地址不能变化),组件就不会被重新渲染了,就能适当的减少我们的开支~

简单示例

下面的例子中,父组件包括了一个循环的定时器,以及一个子组件,不过对子组件传入的是一个固定的值,代码如下

//父组件的代码
import React, {
    useEffect, useState } from "react";
import "./App.css";
import MemoComponent from "./components/MemoComponent";function App() {
   
  const [date, setDate] = useState(0);
  useEffect(() => {
   
    setInterval(() => {
   
      setDate(+new Date());
    }, 1000);
  }, []);
  return (
    <div className="App">
      <p>{
   date}</p>
      <MemoComponent num_array={
   1} />
    </div>
  );
}export default App;
//子组件的代码
export default (props: any) => {
   
  console.log("子组件是否刷新");
  return (
    <>
      <div>这个是其中一个子组件</div>
    </>
  );
};

而由于父组件的一直刷新,所以我们的子组件也会跟着被重新渲染。

此时,我们在考虑如何优化我们的组件时,方向就很明显了,就是当计时器一直刷新的时候,我们的子组件并不需要一直刷新。

那么这时候我们就可以使用memo来跳过子组件的重新渲染。
在这里插入图片描述

memo语法

memo(Component, arePropsEqual)

  • Component
    我们需要进行记忆化的组件,react并不会对其做任何的修改,只是做一个高阶组件的处理,添加完自己的操作后返回给你。

  • arePropsEqual
    可选参数,接受两个参数:组件的前一个 props 和新的 props。一般情况我们不需要去管他,直接忽略他,因为React会使用Object.js去判断前后props是否相同,如果我们手动设置了,会很容易出啥错。

使用memo

理解了memo的语法之后,我们可以在我们的子组件中,使用memo将组件包含起来,使其跳过重复渲染,代码如下

//子组件的代码
import {
    memo } from "react";export default memo((props: any) => {
   
  console.log("子组件是否刷新");
  return (
    <>
      <div>这个是其中一个子组件</div>
    </>
  );
});


此时,由于我们使用memo,子组件避免了无用的重复渲染,所以控制台就不会一直显示子组件被渲染的情况了。
在这里插入图片描述

memo的地址判断

上章提及到,memo会判断我们传入的props是否一样,但当我们传入一个数组(或者对象)的时候,我们会发现我们的页面中,子组件又被重新渲染了。

 //父组件代码
  <MemoComponent num_array={
   [1,2,3,4]} />

这是因为,当我们使用数组或者是对象的时候,由于使用的Object.js在判断对象是否一致的时候,会判断引用地址,所以我们看起来值一样,但是实际上不是同一个(参考js数据类型)。

此时我们可以使用useState来保证数组不被重新赋值。

//父组件的代码
import React, {
    useEffect, useState } from "react";
import "./App.css";
import MemoComponent from "./components/MemoComponent";function App() {
   
  const [date, setDate] = useState(0);
  const [numArray,setNumArray]=useState([1,2,3,4,5])
  useEffect(() => {
   
    setInterval(() => {
   
      setDate(+new Date());
    }, 1000);
  }, []);
  return (
    <div className="App">
      <p>{
   date}</p>
      <MemoComponent numArray />
    </div>
  );
}export default App;

使用了useState之后,由于我们的numArray的每次都是一样的值,一样的地址,子组件就不会被重新刷新了。

所以当你使用memo之后,发现你的项目并没有按照你的预想走的时候,可以检查下你的组件的props。

滥用memo

既然memo有那么多的好处,那为什么我们的项目中没有出现所有的组件都用memo包起来呢?

下面的例子中,我们使用了memo包括了10个组件,且在5s后,我们的父组件会刷新一次页面,而子组件的代码不变。

//父组件代码
import React, {
    useEffect, useState } from "react";
import "./App.css";
import MemoComponent from "./components/MemoComponent";function App() {
   
  const [date, setDate] = useState(0);
  useEffect(() => {
   
    setTimeout(() => {
   
      setDate(+new Date());
    }, 5000);
  }, []);
  return (
    <div className="App">
      <p>{
   date}</p>
      {
   new Array(10).fill(1).map((v) => (
        <MemoComponent />
      ))}
    </div>
  );
}export default App;


下面是使用了memo处理组件的情况下,大概理解为当计时器触发之后,我们的页面花了14ms来重新渲染我们的页面(3915-3901)

在这里插入图片描述

但是当我们去掉了代码中的memo之后,我们发现,计时器触发之后,我们的页面只需要花费11ms(3388-3377)的时间来渲染。
在这里插入图片描述

你以为我会跟借此跟你说,当给每一个组件都添加了memo之后,由于props的判断会导致页面渲染更多时间吗?​

官网上提及到下面这段话

只有当你的组件经常使用完全相同的 props 重新渲染时,并且其重新渲染逻辑是非常昂贵的,使用 memo
优化才有价值。如果你的组件重新渲染时没有明显的延迟,那么 memo 就不必要了。请记住,如果传递给组件的 props
始终不同,例如在渲染期间传递对象或普通函数,则 memo 是完全无用的。这就是为什么你通常需要在 memo 中同时使用 useMemo 和
useCallback

众所周知,我们的react是通过判断props是否发生变化的来判定是否重新渲染组件的,那么我们可以理解,当我们用一个很复杂的props来节省一个很简单的子组件的时候,我们是否就已经失败了呢?

因为我们的react需要先去判断props是否相同,而当花费了30ms的时间在判断props后,结果只是缓存了一行文字…你猜react会不会气抖冷?

毕竟你去吃饭,老板弄了一道高数题,说解出来了,答案就是wifi密码,你千辛万苦解出来,发现老板家的wifi只有1k/s,不单单打扰你跟小姐姐聊天,还用你手机挖矿,你就知道什么感觉了。

至于React的memo是否会占用多一些空间缓存的,由于本人没有具体的demo,所以推荐大家看下github上的答复,就不验证了​​

在这里插入图片描述

一个前端博客,希望能帮到小白们

公众号求关注~

相关推荐

  1. DreamFusion什么地方

    2024-02-06 19:56:01       36 阅读
  2. 现在说 Docker 好,那它有什么弊端?

    2024-02-06 19:56:01       56 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-06 19:56:01       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-06 19:56:01       106 阅读
  3. 在Django里面运行非项目文件

    2024-02-06 19:56:01       87 阅读
  4. Python语言-面向对象

    2024-02-06 19:56:01       96 阅读

热门阅读

  1. c实现顺序表

    2024-02-06 19:56:01       51 阅读
  2. SpringBoot之整合PageHelper分页插件

    2024-02-06 19:56:01       52 阅读
  3. idea 使用外包本地包

    2024-02-06 19:56:01       48 阅读
  4. Web课程学习笔记--CSS盒模型

    2024-02-06 19:56:01       55 阅读
  5. 2.4学习周结

    2024-02-06 19:56:01       42 阅读
  6. k8s学习-Kubernetes Ingress

    2024-02-06 19:56:01       49 阅读
  7. 分布式光伏电站开发存在哪些问题?

    2024-02-06 19:56:01       51 阅读
  8. 【Flink】FlinkSQL的DataGen连接器(测试利器)

    2024-02-06 19:56:01       53 阅读
  9. 为什么SpringBoot胖Jar不好

    2024-02-06 19:56:01       64 阅读
  10. Hadoop-Yarn-启动篇

    2024-02-06 19:56:01       50 阅读
  11. 《Docker极简教程》--Docker基础--Docker的核心组件

    2024-02-06 19:56:01       47 阅读
  12. rust嵌入式开发之RTICvsEmbassy

    2024-02-06 19:56:01       55 阅读
  13. electron实现软件(热)更新(附带示例源码)

    2024-02-06 19:56:01       44 阅读