Skip to main content

Command Palette

Search for a command to run...

React: Hooks - useMemo

Published
1 min readView as Markdown

初學的 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

2 views

More from this blog

Vite: vite-plugin-html的bugs和坑

因為工作要用Vite去生成一個mpa專案且根據頁面不同,動態生成不同 <title> 的HTML,所以就找了一個套件──vite-plugin-html,結果因為套件好像很久沒維護,隨著Vite升級又多了一些有的沒的bug,順手來記錄一下。 template delete 像我第一個就是遇到生成的時候template HTML也會被刪掉,後來看到issue上也有人反映,還好有好心人士解決這個問題,

Feb 26, 20261 min read4

Lun's Blog

82 posts

Notes for web development