跟效能有關的 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
| const NormalComponent = ({ who }) => { let counter = useRef(0) counter.current++ return ( <div> {who} 已經被 render 了 {counter.current} 次 </div> ) }
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> ) }
|
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) 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> ) }
|
多傳一個 function 進去後 memo
就沒效了!
這時候就要回憶一下變數「存值」跟「存址」的差別了,可以複習去這篇 從 Object 的等號來真正理解變數。
總之呢,只要不是 Primitive 型態的變數,它存的都是 reference。所以每次 re-render 的時候都重新宣告了 handleSometing
這個 function,react 在跟上一個 function 比對時就會把這兩個 function 當作是不一樣的東西,可以想成這樣:
1 2 3
| const handleSomething1 = () => {} const handleSomething2 = () => {} console.log(handleSomething1 === handleSomething2)
|
但如果是 Primitive 的變數就不會有這個問題,因為:
1 2 3
| console.log(2 === 2) console.log('2' === '2') console.log(true === true)
|
總之,想用 memo
前得先確認 props 是屬於哪種資料型態,如果是 non-primitive 的話單靠 memo
這個方式是沒用的,得做一些額外處理才行。
第一種解法是在 memo
的第二個參數傳入 function 來自訂比較 props 的方式,第二種方式是改用改用 useCallback
。
這邊會建議用 useCallback
來處理比較好。
可以參考:React 的第六個 hook:useCallback