(2024/04/06更新) 因應React在18後更新了許多不同的語法,更新後的教學之後將陸續放在 新的blog 中,歡迎讀者到該處閱讀,我依然會回覆這邊的提問
但是有的時候我們只負責製作元件,並沒有辦法確認使用這個元件的人是不是在函式定義域內加工元件,這個時候該怎麼辦才能確保這個問題呢?
在Day.19,我們以元件製作者的角度,用memo
去防止使用元件的人讓元件重複渲染。
那如果今天換過來,變成是我們需要在function component定義域內,用函式加工別人製作的元件,在別人製作的元件沒有用memo
的狀況下,要怎麼確保這個加工元件不會被重複渲染呢?
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>
不會有非必要的重複渲染。
我們先把MenuItem的memo拔掉:
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
:
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因為回傳的是第一次渲染時記憶的值,就不會在非必要的時候被重新渲染了