iT邦幫忙

2021 iThome 鐵人賽

DAY 7
0
Modern Web

用30天更加認識 React.js 這個好朋友系列 第 7

Day7-在認識 useMemo 前,先認識 React.memo

今天介紹的是避免重新渲染的 HOC(Higher Order Component) React.memo,透過它可以使我們提升 React 網站的效能。

React.memo

首先,我們知道當父元件 context、state 和 props 其中之一改變時會重新渲染,底下的子元件也會跟著渲染,但實際上父元件只有 state 改變時,是沒有必要渲染底下的子元件的。

功用

而 React.memo 的功用就是避免一些不必要的渲染,透過 React.memo 包住子元件,它會記錄之前的 props 內容,只有當記憶中的 props 改變時(但 state、context 不變)才會重新渲染元件。

語法

React.memo(Component, [areEqual(prevProps, nextProps)]);

React.memo 共接收兩個參數,第一個是要包住的元件,第二個是可以自訂比較 props 的方法,回傳 false 時會重新渲染元件。

特性

React.memo 可惜的是它和 JS 一樣也是透過 by value & by reference 去判斷兩個比較的值是否一樣,也可以說是 shallow compare,所以當 props 是物件型別時,比較的是記憶體位置(by reference)。

父元件重新渲染時,重新提供新的 props 給子元件,如果是物件型別的話,即使內容的值完全一樣,但還是會導致 memo 失效,重新渲染子元件。

此原因也是使用 useMemo 的其中一個原因

解決辦法

  1. 使用 memo 的第二個參數,自訂比較 props 的方法。
  2. 使用 useCallback,因此 memo 與 useCallback 經常被一起使用。

注意

  • 經常變更不一樣的 props 不建議使用 memo。
  • 因為使用 memo 也是需要消耗記憶效能,所以要評估元件的大小是否值得使用。

範例

首先在 App.jsx 內設定好要傳遞的 props,這裡刻意將不會在子元件用到的 step 和 count 傳入,用來示範 React.memo 的第二個參數作用。

App.jsx

import React, { useState } from "react";
import Child from "./Child";
import ChildMemo from "./ChildMemo";

const App = () => {
  const [step, setStep] = useState(0);
  const [count, setCount] = useState(0);
  const [number, setNumber] = useState(0);

  return (
    <>
      <button onClick={() => setStep(step + 1)}>step is : {step} </button>
      <button onClick={() => setCount(count + 1)}>count is : {count} </button>
      <button onClick={() => setNumber(count + step)}>
        number is : {number}
      </button>
      <hr />
      <Child step={step} count={count} number={number} /> <hr />
      <ChildMemo step={step} count={count} number={number} />
    </>
  );
};

export default App;

在 Child.jsx,不使用 React.memo,所以點擊 step 和 count 按鈕改變 state 都會導致 re-render

Child.jsx

import React from "react";

export default (props) => {
  console.log(`Child re-render`);
  return <p>number is : {props.number}</p>;
};

在 ChildMemo.jsx 中,將元件用 memo 包覆,並自定義了 props 比較的方法 isEqual,不管怎麼點擊 step 和 count 按鈕 isEqual 都會回傳 false,只有當 number 更新時才 re-render。

ChildMemo.jsx

import React, { memo } from "react";

const isEqual = (prevProps, nextProps) => {
  if (prevProps.number !== nextProps.number) {
    return false;
  }
  return true;
};

export default memo((props) => {
  console.log(`ChildMemo re-render`);
  return <p>number is : {props.number}</p>;
}, isEqual);

程式範例(codesandbox)


上一篇
Day6-React Hook 篇-useReducer
下一篇
Day8-React Hook 篇-認識 useMemo
系列文
用30天更加認識 React.js 這個好朋友32

尚未有邦友留言

立即登入留言