iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 21
1
Modern Web

從比入門再往前一點開始,一直到深入React.js系列 第 21

【Day.21】React效能 - 用useMemo避免函式非必要的執行

但是有的時候我們只負責製作元件,並沒有辦法確認使用這個元件的人是不是在函式定義域內加工元件,這個時候該怎麼辦才能確保這個問題呢?

在Day.19,我們以元件製作者的角度,用memo去防止使用元件的人讓元件重複渲染。

那如果今天換過來,變成是我們需要在function component定義域內,用函式加工別人製作的元件,在別人製作的元件沒有用memo的狀況下,要怎麼確保這個加工元件不會被重複渲染呢?

useMemo

useMemo是另一個React hook,你可以把useMemo想像成是「用來記憶計算結果」的useCallback。它的語法和useCallback幾乎一模一樣,但useMemo的第一個參數函式必須要有一個回傳值。

const calcRes = useMemo(函式,[相依變數]);

useCallback是用來避免函式被重複定義,而useMemo是用來避免函式一直被重複執行

useMemo會把上次函式的回傳值記憶起來,當元件被更新、但相依的變數沒有被改變時,useMemo就不會再執行一次函數,而是直接把上一次記憶的回傳值丟出去

也因為這個特性,如果你在useMemo回傳的是函式,它就會跟useCallback一樣。

我們可以利用useMemo,避免執行過程需要花費大量資源的函式被非必要的執行。例如在剛剛的case中,就能用useMemo告訴React要把加工完的<MenuItem>陣列記憶起來。讓React在menuItemWording沒有被改變的時候,不要執行加工製造<MenuItem>的這個函式,而是直接回傳上次加工完的結果。由於最後React判斷MenuItem和上次的reference一樣,藉此就能讓<MenuItem>不會有非必要的重複渲染。

加入useMemo到先前的程式碼中

我們先把MenuItem的memo拔掉:

  • src/component/MenuItem.js
import React from 'react';

const menuItemStyle = {
    marginBottom: "7px",
    paddingLeft: "26px",
    listStyle: "none"
};

function MenuItem(props){
    return <li style={menuItemStyle}>{props.text}</li>;
}

export default MenuItem;

然後在MenuPage把加工wording成MenuItem的地方加入useMemo:

  • src/component/Menu.js
import React, {useState, useRef, useCallback, useMemo} from 'react';

import MenuItem from '../component/MenuItem';
import Menu from '../component/Menu';
import { OpenContext } from '../context/ControlContext';

let menuItemWording=[
    "Like的發問",
    "Like的回答",
    "Like的文章",
    "Like的留言"
];

const MenuPage = () =>{
    const [isOpen, setIsOpen] = useState(true);

    const renderCounter = useRef(0);
    renderCounter.current++;

    const handleClick = useCallback(() => {
        console.log("counter is " + renderCounter.current);
    },[renderCounter]);
    
    const menuItemArr = useMemo(()=> {
        return menuItemWording.map((wording) => <MenuItem text={wording}/>)
    },[]);

    return (
        <OpenContext.Provider value={{ 
            openContext: isOpen, 
            setOpenContext: setIsOpen
        }} >
            <Menu title={"Andy Chang的like"}>
                {menuItemArr}
            </Menu>
        </OpenContext.Provider>
    );
}

export default MenuPage;

此時MenuItem因為回傳的是第一次渲染時記憶的值,就不會在非必要的時候被重新渲染了


上一篇
【Day.20】React效能 - 用useCallback避免函式的重新定義
下一篇
【Day.22】React效能 - 如何處理useContext的效能問題
系列文
從比入門再往前一點開始,一直到深入React.js30

尚未有邦友留言

立即登入留言