# React 優化項目(三):useMemo
###### tags: `React` `Optimize` `Hooks` `useMemo` `OptimizeRender`
React 優化項目系列,減少大量且不必要的元件重新渲染週期
#### [React 優化項目(一):React.memo](https://hackmd.io/@yellow/optimize-render)
> 如何觸發更新的 <br />紀錄 props value
#### [React 優化項目(二):useCallback](https://hackmd.io/@yellow/optimize-render-usecallback)
> 紀錄 func 記憶體位置
#### [React 優化項目(三):useMemo](https://hackmd.io/@yellow/optimizer-render-useMemo)
> 紀錄 object value 避免重複執行相關處理
<br />
## `State` 的更新流程以及批次更新
開始前先來了解 State 的更新流程,以及批次更新。
- State 的更新是不及時的
- 當 state 觸發更新執行 `re-executed`,state 才會是最新的。
```
...
const [showParagraph, setShowParagraph] = useState(false);
...
const toggleParagraph = useCallback(() => {
allowToggle && setShowParagraph((pre) => !pre);
// 假如 setShowParagraph 有執行,
// 在這裡印出 showParagraph 一樣會是 false
}, [allowToggle]);
...
}
```
那如果更新的 state 不止一個的時後,元件是不是就會一直跑 `re-executed`
像是下面範例,更新兩個 state,所以跑兩次 `re-executed`?<br />
不會的,在同一個 block 裡,如果沒有穿插其他非同步項目,React 會將這兩個 state 在同一個 `re-executed` 行程裡一起做更新。
```
...
const navHandle = (navPath) => {
// 是不是這裡就會 re-executed 兩次?
setCurrentNav(navPath)
setDrawerIsOpen(false)
};
...
```

<br />
## 範例測試
> 再開發過程中,常常需要對 origin list 做一些處理,可能是 sort, format 或是對某欄位進行複雜運算,如果 origin list 沒有改變,是否我的處理也可以不需要再跑一次。
在 `RenderTry` 引入 `DemoList`
```
export default function RenderTry() {
...
console.log('APP RUNNING');
const [listTitle, setListTitle] = useState('My Title');
const changeTitleHandle = useCallback(() => {
setListTitle('New Title!');
}, []);
...
<h2>{listTitle}</h2>
// 傳進 items
<DemoList items={[5, 3, 1, 10, 9]} />
<Button onClick={changeTitleHandle}>changeTitle</Button>
...
```
#### 子元件 `DemoList`
```
import React from 'react';
function DemoList({ items }) {
console.log('DEMO LIST RUNNING!');
const sortList = items.sort((a, b) => a - b);
return (
<>
<ul>
{sortList.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</>
);
}
// 加上 React.memo
export default React.memo(DemoList);
```
#### 執行更新觀察
可以看到 重新整理執行一次更新,點選 btn 更新 state 時,`DemoList` 也再執行了一次。
:::info
這是因為 javascript Object 是傳址的特性,所以對 React.memo 來說,這個 object 是不一樣的
:::

## 使用 React.useMemo()
> 紀錄 object value 避免重複執行相關處理
[可以先了解基本用法 >> ](https://hackmd.io/@yellow/react-hooks#useMemo-%E8%A8%98%E6%86%B6%E5%80%BC)
#### 父層元件 RenderTry 修改
```
...
// 將原本傳進去的 items 包裹在 useMemo 裡
const items = useMemo(() => [5, 3, 1, 10, 9], []);
...
<DemoList items={items} />
```

可以看到 DemoList 就不再重複執行了。
但 useMemo 主要用在當前元件重新渲染時,通過記憶職來避免重複執行,他是無關父元件的。
所以較正確使用,應該是將 `useMemo` 加在 `DemoList 裡面`
#### 修改 DemoList 元件
```
import React, { useMemo } from 'react';
function DemoList({ items }) {
// 使用 useMemo 儲存記憶值
const sortList = useMemo(() => {
console.log('sortList!');
return items.sort((a, b) => a - b);
}, [items]);
```
:::info
這麼一來當我的 `DemoList` 外層 props items 未曾改變,但再做其他更新的事情時,就可以避面 sortList 重新做計算的動作!
:::