今天介紹的是避免重新渲染的 HOC(Higher Order Component) 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 在比較 props 改變時,背後是使用 Object.is()
去判定,當 props 是原始型別時,比較的是值是否相同,當 props 是物件型別時,比較的是記憶體位置(by reference)。
所以父元件重新渲染時,重新提供新的 props 給子元件,如果是物件型別的話,即使內容的值完全一樣,但還是會導致 memo 失效,重新渲染子元件。
此原因也是使用 useMemo 的其中一個原因
首先在 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);