# [React] `React.memo` 比對 component `props` 是否有變動,有變動才 re-render ###### tags: `React` `前端筆記` `Udemy 課程筆記` 在 React 中,只要 state 有變更的話,該 component 及其子孫 components 都會 re-render(重新叫用一次),因為這個緣故,有時候會讓子孫 components 無謂地 re-render。如果開發者確認其子孫 components 的 `props` 都是一樣的話,就可以用 `React.memo` 幫助 React 記憶 `props`,在 `props` 與上次 re-render 不同時才要重新 re-render。 ## 使用方式 `React.memo` 是一個 HOF(high order function),且是對「component」使用,所以直接將 component 放入 `React.memo()` 內,接下來 React 就會檢查該 component 的 `props` 是否改變,如果有改變才會 re-render(重新執行該 component 的計算),如未改變則省略 re-render,直接拿上一次記錄的結果(所以並不會重新執行該 component 的計算)。 除此之外,`React.memo` 還可以給第二個參數(dependency callback),開發者可以客製化控制比對結果(如函式回傳 `true` 代表不需要重新 re-render,`false` 則代表需要 re-render) ```javascript= import React from "react"; const customizedComparison = (prev, next) => { return prev.isActive === next.isActive; }; const Demo = (props) => { console.log("demo render"); return <h1>This is Demo</h1>; }; /* React.memo 是 HOF,所以需要將 functional compoennt 放在裡面 React 就會在 render 這個 component 之前比對 props 是否有改變 */ export default React.memo(Demo, customizedComparison); /* 不使用 React.memo 就會看到即便都給相同的 props,因為其 parent component state 更新,所以這個 component 也會更新(因為 parent re-render 也會影響其子孫 components re-render) */ // export default Demo; ``` ### 但如果 props 為非基礎型別的話(比如說傳 pointer function 給 child component),使用 `React.memo` 就沒用了 以下方的例子來看,即便使用 `React.memo` 查看 `props` 是否有改變,但每次 parent component 的 state 改變的話,這個 component 還是會重新 re-render。 ```javascript= import React from "react"; const Button = (props) => { console.log("button render"); // 從 parent component 接收 pointer function as props return <button onClick={props.onToggleActive}>{props.children}</button>; }; export default React.memo(Button); ``` ### 因為 `React.memo` 採用的是類似 `===` 的比對策略,且每次 re-render 都在建立新的 scope ```javascript props.prevState.property === props.nextState.property ``` 每一次 re-render 都是重新執行函式內的所有程式碼,而且都會建立不同的 scope。 基礎型別因為值的存取和物件型別不同(不會存到 Heap),因此互相比對時就只會單純地互相比較值。但物件型別的值其實是保存該物件在 Heap 的位置,所以每次 re-render 物件型別就是在建立新的 Heap,所以單純用 `React.memo` 檢查 pointer function 才會無法達到預期中的結果。 ### `React.memo` + `useCallback` 讓 `React.memo` 可以比對 pointer function 將函式用 `useCallback` 包起來並設定 dependency array,React 會確保除非 `useCallback` 的 dependency array 有更動,要不然每次 re-render 後該函式都是原來的函式(意即相同的 Heap)。 所以只要 `useCallback` 的 dependency array 不變 = `React.memo` 收到的 pointer function 不變 = `React.memo` 包的 component 不會 re-render。 ```javascript= // src/app.js // ... export default function App() { console.log("app render"); const [isActive, setIsActive] = useState(false); /* dependency array 給 [] -> 該 callback 永遠不會改變, 因為每次 re-render 時 [] 不會有任何的變更 */ const clickHandler = useCallback(() => { setIsActive((prevState) => !prevState); }, []); return ( <div className="App"> <Button onToggleActive={clickHandler}>Click me</Button> {/*把 props 寫死,即可看到 Demo component 不會重新 render*/} <Demo isActive={false} /> </div> ); } ``` ## 使用 `React.memo` 的小撇步 以下情況並不適合使用 `React.memo`: 1. 該 component 不需要進行很多的運算或者該 component 沒有擁有冗長的子孫 components 2. 該 component 會頻繁地收到不同的 props ### Why? 因為「記憶」也是需要效能的,因此需要評估記憶是否真的 Z > B。 [完整範例](https://codesandbox.io/s/udemy-react-section-155-react-memo-5qd6q9?file=/src/App.js) ## Recap 1. `React.memo` 為 HOF,包裹 component 2. `React.memo` 會記憶 `props`,待 `props` 有變更時才會 re-render 3. 可以把比對想像成 `===` 因此如果要傳 pointer function 必須要先針對 pointer function 使用 `useCallback` ## 參考資料 1. [React - The Complete Guide (incl Hooks, React Router, Redux) - sec. 155](https://www.udemy.com/course/react-the-complete-guide-incl-redux/learn/lecture/25599594#questions/15071992) 2. [Day7-在認識 useMemo 前,先認識 React.memo](https://ithelp.ithome.com.tw/articles/10268792) 3. [Use React.memo() wisely](https://dmitripavlutin.com/use-react-memo-wisely/)
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up