[Note] React - Hooks: useMemo


Posted by urlun0404 on 2022-07-22

初學的 useState 其實可以做蠻多事情,但 useState 有個很大的缺點是 每次 state 改變,整個component都會re-render一次,這樣會有什麼問題?為什麼需要學習 useMemo


useMemo 是一種優化(optimisation)方法,它保證只針對 Dependency 有改變時,才會取得上次render的值去呼叫函數。

用這部影片的範例來說明,假設現在我們有個Counter component,並且做了兩個計數器:

import {useState} from 'react';

export default function Counter () {
    const [counterOne, setCounterOne] = useState(0);
    const [counterTwo, setCounterTwo] = useState(0);

    const isEven = () => {
        let i = 0;
        while( i < 100000000000 ) ++i;   // 每個state改變都會re-render整個component並invoke isEven
        return counterOne % 2 === 0;
    }

    return (
    <>
        <div>
            <button onClick={()=>setCounterOne(counterOne+1)}>Counter1: {counterOne}</button>
            <span>{isEven() ? 'Even' : 'Odd'}</span>
        </div>
        <div>
            <button onClick={()=>setCounterTwo(counterTwo+1)}>Counter2: {counterTwo}</button>
        </div>
    </>
    );
}

假設初始值是0,我點擊 Counter1 按鈕,可預期按完後要過一陣子才會顯示 1;但如果只用 useState 的問題就是:即使我只點擊 Counter2 按鈕,同樣會過一陣子才會出現1

理由在於:只要component的任一個 state 改變,整個 component 都會re-render,所以不論點擊哪顆按鈕,都會呼叫 isEven 這個很expensive的函數。

我們希望只有當我點擊 Counter1 按鈕,才會呼叫 isEven 函數,這個時候能用 useMemo 來幫忙:

import {useState, useMemo} from 'react';

export default function Counter () {
    const [counterOne, setCounterOne] = useState(0);
    const [counterTwo, setCounterTwo] = useState(0);

    const isEven = useMemo(() => {
        let i = 0;
        while( i < 100000000000 ) ++i;
        return counterOne % 2 === 0;
    }, [counterOne]);   // isEven只在counterOne改變時才被invoke

    return (
    <>
        <div>
            <button onClick={()=>setCounterOne(counterOne+1)}>Counter1: {counterOne}</button>
            <span>{isEven ? 'Even' : 'Odd'}</span>
        </div>
        <div>
            <button onClick={()=>setCounterTwo(counterTwo+1)}>Counter2: {counterTwo}</button>
        </div>
    </>
    );
}

這個修改會改變兩件事情:

  1. 點擊 Counter1 按鈕才會呼叫 isEven ,且 useMemo 會取得上一次re-render後的 Counter1 值去計數;
  2. 點擊 Counter2 按鈕並不會呼叫 isEven ,所以可以預期點擊後不會過一陣子才看到改變後的數字。


References


#frontend #React #hook #memo #useMemo #note







Related Posts

webpack 新手教學之淺談模組化與 snowpack

webpack 新手教學之淺談模組化與 snowpack

#Tailwind #Webpack resolve-url-loader cannot operate: CSS error

#Tailwind #Webpack resolve-url-loader cannot operate: CSS error

function 額外補充

function 額外補充


Comments