---
# System prepended metadata

title: 從 Hooks 開始，讓你的網頁 React 起來系列的閱讀筆記（21-22）
tags: [React, 閱讀筆記, 前端筆記]

---

# 從 Hooks 開始，讓你的網頁 React 起來系列的閱讀筆記（21-22）
###### tags: `閱讀筆記` `前端筆記` `React`
## 完成章節：
- 21-22（2021/11/28）
## `useMemo` 保存「值」

`useMemo()` 可以輸入兩個參數，第一個為函式，第二個為 dependency（Array），與 `useCallback()` 很像，但是`useMemo()` 會回傳第一個參數（也就是函式）執行的結果（值），如果 dependency 沒有改變的話就不會重新計算（換言之就是如果 dependency 沒有改變就連算都不算，直接回傳同樣的值！）

範例是用 `props` 接受父層的資料，如果父層的資料有改變，那麼就會觸發 `setSometing` re-render Components，屆時 WeatherIcon Component 必定會重新執行，就會得到父層新的資料，也因為 `useMemo` 是以原本的未改變的資料為 dependency，所以 `useMemo` 必然會重新運算得到新的值，等到 render 結束就會觸發 `useEffect` （因為其 dependency） 也改變了，所以就會執行 `useEffect` 的任務（觸發 `setSometing` re-render WeatherIcon component）。

```javascript=
// WeatherIcon.js

// ...

export function WeathcerIcon({ moment, currentWeatherCode }) {
  const [currentWeatherIcon, setCurrentWeatherIcon] = useState("isClear");
  // 每次 render 都會執行 useMemo，但是只要 dependency 沒變就不會運算，直接給同樣的值
  const theWeatherIcon = useMemo(() => weatherCode2Type(currentWeatherCode), [
    currentWeatherCode
  ]);
  useEffect(() => {
    console.log("in useEffect from WeatherIcon");
    const currentWeatherIcon = weatherCode2Type(currentWeatherCode);
    console.log(currentWeatherIcon);
    setCurrentWeatherIcon(currentWeatherIcon);
    // 用 useMemo 回傳的值來控制是否要觸發 setCurrentWeatherIcon 觸發 re-render
  }, [theWeatherIcon]);
  return (
    <IconContainer>{weatherIcons[moment][currentWeatherIcon]}</IconContainer>
  );
}
```

*`useMemo` 是 rendering Component 時就會執行，只是會看 dependency 有沒有變才會運算第一個參數（也就是函式）*

### 用 `useMemo` 可以省略重複地複雜運算

1. first render component (WeatherApp)
2. 執行 render
3. first render component (WeatherIcon)
4. 執行 render 
5. WeatherApp 的 first render component 結束（觸發 useEffect）
6. 收到資料了
7. WeatherIcon 的 first render component 結束（觸發 useEffect）
8. WeatherApp 收到 API 資料觸發 `setSometing` re-render Component
9. 執行 render 
10. 執行 WeatherIcon 的 render
11. 因為 WeatherIcon 的 `useEffect` 的 dependency 有被更動，所以再次觸發 `setSometing` re-render Component
12. 繼續 re-render WeatherApp 剩下的 component 

![](https://i.imgur.com/utK2LGI.png)

如果之後拉 API 沒改變 WeatherIcon `useMemo` 的 dependency（也就是地址），就不會觸發 WeatherIcon 的 `useEffect`，自然就只會因主要 Component WeatherApp 有因獲得 API 回應觸發 `setSometing` 執行的 re-render 順道 re-render WeatherIcon 一次而已。
![](https://i.imgur.com/3kz4JRe.png)

**仔細發現就可以看到 LIFO（Last in, First out）的觀念，functional component 就是 function**

因為 WeatherApp 中的 `useEffect` 的任務是非同步的，所以會先執行 WeatherIcon render 後的 `useEffect` 後再回頭執行 WeatherApp `useEffect`。==要注意只要 Component first render 結束後就會觸發 `useEffect`。==
知道主要 Component 及小 Component 的 render + `useEffect` 執行順序之後再安排程式流程應該會有所幫助。

```javascript=
// WeatherApp. js

// ...

  const fetchData = useCallback(() => {
    const fetchingData = async () => {
	  // await 保證的是這段執行完畢（也就是兩個 Promise 都取得回應後 + .then 結束後）才會繼續往下執行
      const [currentWeather, weatherForecast] = await Promise.all([
        fetchCurrentWeather(),
        fetchWeatherForecast()
      ]);

      setWeatherElement({
        ...currentWeather,
        ...weatherForecast
      });
    };

    fetchingData();
  }, []);

useEffect(() => {
    console.log("execute function in useEffect");

    fetchData();
  }, [fetchData]);

// ...
```

## 參考資料
1. [從 Hooks 開始，讓你的網頁 React 起來 21-22](https://ithelp.ithome.com.tw/users/20103315/ironman/2668)
2. [useMemo](https://zh-hant.reactjs.org/docs/hooks-reference.html#usememo)
