React.Memo优化组件

166 阅读3分钟

涉及到的优化方式

为什么我们需要去优化React组件?,React在组件的渲染上会有什么问题?

  • React组件有个特性,在不进行优化处理时父组件更新的时,子组件一定会重新渲染

优化的角度

  • 减少组件的不必要渲染
  • 提高组件的可读性以及减少复杂度,避免出现难以发现的bug

组件层面的优化

React.memo

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

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

这我来解释下

  • 组件的数据来源一般情况下有两种stateprops
  • 在函数组件中state是做优化的,如果更新的state数据通过对比,未发生改变,组件就不会重新渲染
  • props函数组件本身是没有做任何处理的,只要props更新了,那组件必更新

上代码

import { useState } from "react";

function Com1(props: any) {
  const [com1Data, setCom1Data] = useState(0);

  console.log("Com1===> 重新渲染了");
  return (
    <>
      <span>Com1的数据: {com1Data}</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      <br />
      <br />
      <button
        onClick={() => {
          setCom1Data(com1Data + 1);
        }}
      >
        点击更改Com1数据
      </button>
      <br />
    </>
  );
}

function Com2(props: any) {
  const [com2Data, setCom2Data] = useState(0);

  console.log("Com2===> 重新渲染了");
  return (
    <>
      <span>Com2的数据: {com2Data}</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      <span>父组件的数据: {props.data}</span>
      <br />
      <br />
      <button
        onClick={() => {
          setCom2Data(com2Data + 1);
        }}
      >
        点击Com2数据
      </button>
    </>
  );
}

function App() {
  const [appData, setAppData] = useState(0);

  console.log("App===> 重新渲染了");
  return (
    <div className="App">
      <button
        onClick={() => {
          setAppData(appData + 1);
        }}
      >
        点击更改app数据
      </button>
      <br />
      <br />
      <Com1 />

      <br />

      <Com2 data={appData} />
    </div>
  );
}

定义了三个组件具有嵌套结构

image.png

  • 点击Com1按钮

image.png

  • 点击Com2按钮

image.png

  • 点击app按钮

image.png

重点来了😶‍🌫️😶‍🌫️😶‍🌫️😶‍🌫️

可以发现Com1组件重新渲染了这就是问题所在,也是没有必要的渲染,这个时候React.Memo就该出手了

function Com1(props: any) {
  const [com1Data, setCom1Data] = useState(0);

  console.log("Com1===> 重新渲染了");
  return (
    <>
      <span>Com1的数据: {com1Data}</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      {/* <span>父组件的数据: {props.data}</span> */}
      <br />
      <br />
      <button
        onClick={() => {
          setCom1Data(com1Data + 1);
        }}
      >
        点击更改Com1数据
      </button>
      <br />
    </>
  );
}
// 小小的改动大大的变化🎈🎈🎈
export default React.Memo(Com1)
  • 点击App按钮

image.png

发现没Com1并没有重新渲染 至于App 和Com2 它们的重新渲染时必须的因为自变量发生了变化

如果用React.memo包裹Com2呢?

function Com2(props: any) {
  const [com2Data, setCom2Data] = useState(0);

  console.log("Com2===> 重新渲染了");
  return (
    <>
      <span>Com2的数据: {com2Data}</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      <span>父组件的数据: {props.data}</span>
      <br />
      <br />
      <button
        onClick={() => {
          setCom2Data(com2Data + 1);
        }}
      >
        点击Com2数据
      </button>
    </>
  );
}
// 这里变了呦🎈🎈🎈🎈🎈
expost default React.Memo(Com2)

function App() {
  const [appData, setAppData] = useState(0);
  // 这里变了呦🎈🎈🎈🎈🎈
  const [appData1, setAppData1] = useState(0);

  console.log("App===> 重新渲染了");
  return (
    <div className="App">
      <button
        onClick={() => {
          setAppData(appData);
        }}
      >
        点击更改appData数据
      </button>
        // 这里变了呦🎈🎈🎈🎈🎈
      <button
        onClick={() => {
          setAppData1(appData1 + 1);
        }}
      >
        点击更改appData1数据
      </button>
      <br />
      <br />
      <Com1 />

      <br />

      <Com2 data={appData} />
    </div>
  );
}
  • 点击更改appData数据

image.png

发现没Com2并没有重新渲染: 因为它所依赖的props数据没有发生变化

  • 点击更改appData1数据

发现没有任何组件重新渲染 Why???? :在App组件中state是做优化的,如果更新的state数据通过对比,发现数据没变,所以App组件也没有渲染。

那如果传入的props是引用数据类型呢?React.memo()的表现是怎么样的?

  • 如果是引用类型在在对比数据时:是对比引用地址的,所以在设置引用类型的数据时,建议还是赋值一个新的地址,以免设置后无法更新视图
  • 如果依赖的props中有函数,怎样固定函数地址,因为在每次渲染时都会生成不同地址的函数??这就是useCallback()的需要做的事情了

Memo的其他细节,可以在官网中查看到

下次分享下关于useCallbackuseMemo😁😁😁😁😁😁😁😁😁😁