# React: Hooks - useMemo

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

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

用這部[影片](https://www.youtube.com/watch?v=qySZIzZvZOY)的範例來說明，假設現在我們有個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` ，所以可以預期點擊後不會過一陣子才看到改變後的數字。
<br/>

---
References
- [React Hooks Tutorial - 27 - useMemo Hook@Codevolution](https://www.youtube.com/watch?v=qySZIzZvZOY)
