前幾天我們都在介紹這些優化 React
效能的好東西,像是 memo
、useCallback
、useMemo
,在我們的組件需要重複渲染時,不會被沒有改變的狀態去影響到我的子組件,進而減少不必要的渲染
所以我們就在 React
專案中全部都加上,memo
、useCallback
、useMemo
,這樣一定可以有效的減少我的重複渲染的問題!
但是真的是這樣嗎? 其實在追求效能的背後都是滿滿的成本
這兩個大家應該會比較好區分
useMemo
通常會用在處理複雜且耗時計算時使用useCallback
需要傳遞一個 function
給子層,且不會受到父層重新渲染時影響
但是為何在上段結尾會說 追求效能的背後都是滿滿的成本
讓我們來看看如何定義 useCallback 以及 useMemo
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
在使用這兩個
我們必須先去定義 function 以及 一個 dependency Array,在任何的程式語言中只要多一行 code,就會增加執行時的成本,更何況這兩個 Hook 還會幫你記住 記憶體位址,並且去比對前次狀態和現在狀態是否又不同,再去決定是否要渲染畫面
所以除非是需要大量計算,或是畫面不需要常常重新渲染的話,否則可以不用使用到這兩個 HooK
同理使用 memo
時,React一樣會幫記住 這次渲染的 props 是什麼
,並且去做比對上一次 prop
和這次是否有不同,再決定是否渲染
如果我的每個組件都使用 memo
,因為 React 需要記住東西太多了,所以可能反而會在初次渲染時變慢
所以我認為我們不該去濫用 memo
,我們應該思考的是如何將我們的元件規劃好!
其實這是最理想的狀況,就是設計好元件與元件的關係,盡量不要造成無謂的重複渲染
舉個例子
我們可以看到每當點擊按鈕時 執行 setCount
,就會造成整個元件渲染,盡而引響子層渲染,這樣就會操成無謂的渲染
const App = () => {
const [count, setCount] = useState(0)
return (
<>
<div>
<button onClick={() => {setCount(perv => perv + 1)}}>+</button>
{ count }
</div>
<SlowComponent />
</>
)
}
所以我們可以改成這樣,將元件切分的更細,就不會相互引響
const App = () => {
return (
<>
<Counter />
<SlowComponent />
</>
)
}
const Counter = () => {
const [count, setCount] = useState(0)
return (
<div>
<button onClick={() => {setCount(perv => perv + 1)}}>+</button>
{ count }
</div>
)
}
或是你可以你可以透過 children,來避免相互引響而造成的重複渲染
const App = () => {
return (
<>
<Counter>
<SlowComponent />
</Counter>
</>
)
}
const Counter = ({ children }) => {
const [count, setCount] = useState(0)
return (
<div>
<button onClick={() => {setCount(perv => perv + 1)}}>+</button>
{ count }
{ children }
</div>
)
}
全部 component 都加上 memo
vs 不使用 memo
先說 全部 component 都加上 memo
的觀點
以這篇文章 Why We Memo All the Things 的作者,他認為全部的 component 都需加上
memo
,而且所有的props
都使用useMemo
,他認為這樣可以增加開發者的開發時間,而且開發者也不用去思考我這邊是否要是否要使用 memo useMemo 或是 useCallback
不使用 memo
的觀點則是
認為大部分的
memo
useMemo
和useCallback
都該移除,因為他們可能沒有帶給效能上的優化,反而增加了初次渲染的負擔,並增加了程式的複雜程度
因為大量使用memo
useMemo
和useCallback
,首次渲染時,因為佔用了太多記憶體位址,所以會影響初次渲染的速度
但是不使用 memo
的話可能會將元件切分成更小的元件,會增加開發時間,命名難度,程式碼可讀性
所以其實兩派都各有優缺點,就看看你喜歡哪派了~
其實一開始認識到 memo
useCallback
useMemo
會覺得好厲害,那我可以在專案上全部都使用嗎?
而我的觀點是,還是要是專案的實際情況為例,因為其實 re-render 重複渲染並不是壞事
而是在你該重複渲染的組件就讓他渲染吧,而不該渲染的元件就設計成讓他最低限度的炫染就好
這三個武器一定會有適合的使用時機~