React 的第五個 hook:memo

跟效能有關的 hook 之一。

簡述

把 Component 給記起來,如果 props 沒有變就不會 re-render

一般在父元件的 state 改變時,會連帶底下的所有子元件 re-render,可是有些時候不需要阿,子元件根本就沒改到什麼內容,幹嘛 re-render?

這時候就可以用 memo,來對子元件做一層包裝,讓它只有在 props 改變時才會跟著 re-render

這邊寫了一個簡單的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 一般的 component
const NormalComponent = ({ who }) => {
// 計數器
let counter = useRef(0)
// 每次 re-render 就遞增
counter.current++
return (
<div>
{who} 已經被 render 了 {counter.current} 次
</div>
)
}
// 用了 memo 的 component
const MemoComponent = memo(NormalComponent)

function App() {
const [value, setValue] = useState()

const handleChange = (e) => setValue(e.target.value)

return (
<div className='app'>
<input type='text' value={value} onChange={handleChange} />
<NormalComponent content='一般的 Component' />
<MemoComponent content='Memo 後的 Component' />
</div>
)
}

memo-01

NormalComponent 因為沒有用 memo 包裝,所以每次父元件 re-render 時都會連帶影響,而 MemoComponent 有用 memo 包裝,並且它的 props(content) 在 re-render 時並沒有改變,所以不會被重新渲染。

可是要特別注意,當 props 是 Object 的時候,結果可能就會出乎你預料:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function App() {
const [value, setValue] = useState()
const handleChange = (e) => setValue(e.target.value)
// 一個要當作 props 的 function
const handleSomething = () => {}

return (
<div className='app'>
<input type='text' value={value} onChange={handleChange} />
{/* 傳進去 */}
<NormalComponent content='一般的 Component' someObject={handleSomething} />
<MemoComponent content='Memo 後的 Component' someObject={handleSomething} />
</div>
)
}

memo-02

多傳一個 function 進去後 memo 就沒效了!

這時候就要回憶一下變數「存值」跟「存址」的差別了,可以複習去這篇 從 Object 的等號來真正理解變數

總之呢,只要不是 Primitive 型態的變數,它存的都是 reference。所以每次 re-render 的時候都重新宣告了 handleSometing 這個 function,react 在跟上一個 function 比對時就會把這兩個 function 當作是不一樣的東西,可以想成這樣:

1
2
3
const handleSomething1 = () => {}
const handleSomething2 = () => {}
console.log(handleSomething1 === handleSomething2) // false

但如果是 Primitive 的變數就不會有這個問題,因為:

1
2
3
console.log(2 === 2) // true
console.log('2' === '2') // true
console.log(true === true) // true

總之,想用 memo 前得先確認 props 是屬於哪種資料型態,如果是 non-primitive 的話單靠 memo 這個方式是沒用的,得做一些額外處理才行。

第一種解法是在 memo 的第二個參數傳入 function 來自訂比較 props 的方式,第二種方式是改用改用 useCallback

這邊會建議用 useCallback 來處理比較好。

可以參考:React 的第六個 hook:useCallback

React 的第六個 hook:useCallback 什麼是 Virtual DOM?
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×