前一篇我們詳細解釋了 useMemo
和 useCallback
的運作原理和邏輯,今天我們就來檢討一些使用不好的例子。
首先,我們都已經知道這兩個 hook 是來幫助我們處理深入比較傳入參數的新舊值,是否完全相等,那麼這樣的處理我們在 useEffect
的篇章也提及了怎麼實現。
那麼我們都有了基本的概念,會理解到每做一次的 deep compare 就會觸發多一層的歷遍,那有大O基礎的朋友應該就不難理解應該要如何避免。
簡單來說,就要避免像俄羅斯套娃那種層層堆疊的處理,既然每次的使用都是指數型耗能,那肯定要用在刀口上才行,那我們來看看一些使用的好與不好的範例吧!
比較不好的:
// bad
// 數字相加
const sum = useMemo(() => num1 + num2, [num1, num2])
// 這個就是比較差的使用情況,都是可以直接運算出結果的就不需要在進一次
// 改成
const sum = num1 + num2;
// 字串相加
const fullstr = useMemo(() => `${str1} ${str2}`, [str1, str2])
// 一樣的情況在字串上也都是一樣的
// 改成
const fullstr = `${str1} ${str2}`;
比較好的:
// good
// 陣列型別
const doubleNums = useMemo(() => nums.map((num) => num * 2), [nums])
// 處理陣列,同樣也可以用在filter, reduce...其他的方法情況
// 物件型別
const playerInfo = useMemo(() => ({
name
hp,
mp,
carryweight: hp*1.5 + mp*2
}), [name, hp, mp])
// 陣列和物件都是by reference的,在處理上會顯得比較有意義一點
// 此外,也要盡量避免相互層套
const playerInfo2 = useMemo(() => ({
name,
hp,
mp,
carryweight: hp*1.5 + mp*2,
doubleNums // 已經是useMemo處理過的東西
}), [name, hp, mp, doubleNums])
那我們接著來看看 useCallback
以上一篇為例。
比較不好的
import React, { useState, useCallback } from 'react';
const ParentComponent = () => {
const [count, setCount] = useState(0);
// 比較不好的
const increment = useCallback(() => {
// 直接拿取count來修改
setCount(count + 1);
// dependencies array又必須拿count,那每次執行又會重建一次function
}, [count]);
return (
<div>
<ChildComponent onClick={increment} />
<p>Count: {count}</p>
</div>
);
};
const ChildComponent = ({ onClick }) => {
console.log('我被渲染啦');
return <button onClick={onClick}>Increment</button>;
};
export default ParentComponent;
比較好的
import React, { useState, useCallback } from 'react';
const ParentComponent = () => {
const [count, setCount] = useState(0);
// 比較好的作法
const increment = useCallback(() => {
// 透過callback拿取前值來修改
setCount((prevCount) => prevCount + 1);
}, []);
return (
<div>
<ChildComponent onClick={increment} />
<p>Count: {count}</p>
</div>
);
};
const ChildComponent = ({ onClick }) => {
console.log('我被渲染啦');
return <button onClick={onClick}>Increment</button>;
};
export default ParentComponent;
所以在 useCallback
的使用上是要考量你的 function 需不需要大量受到其他參數而重建,如果需要那你乾脆直接往下傳即可,如果不需要你可以考慮擺空陣列 []
,或是比較不常被更動的參數為主。
在使用上簡單概括:
useMemo
useCallback
也不需要過度使用,每次使用就是在進一次深度的比較機制,執行的話也比較耗效能,但好處是能避免不需要重複的情況。