2023-05-17
#ReactReact useMemo 筆記
#範例
Demo: https://codesandbox.io/s/usememo-nffpkl?file=/src/App.js:0-716
#畫面說明:
- 有個 input,我們可以輸入數字,輸入後在 Double Value 就會顯示所輸入數字乘以二的結果
- 有個 change theme 按鈕,可把畫面轉為暗色模式(黑背景白字)
#Code 說明:
- 我們用一個
slowFunction
來模擬複雜、要跑很久的 function - input 填入數字後,會使
value
狀態改變 - 每次 render 的時候都會執行
slowFunction
,在 demo 中可以看到輸入後,都要等約一秒,Double value 才改變 - 在切換 theme 的時候也一樣,點擊按鈕後也會有 lag,主題顏色才改變,這是因為只要 state 有改變,就會觸發畫面的 re-render,
slowFunction
都會再執行一次
function slowFunction(num) {
for (let i = 0; i < 1000000000; i++) {}
return num * 2;
}
export default function App() {
const [value, setValue] = useState(0);
const [darkTheme, setDarkTheme] = useState(false);
const doubleValue = slowFunction(value);
const themeStyle = {
color: darkTheme ? "#fff" : "#1b1b1b",
background: darkTheme ? "#1b1b1b" : "#fff"
};
return (
<div className="App" style={themeStyle}>
<input
type="number"
value={value}
onChange={(e) => {
setValue(e.target.value);
}}
/>
<div>Double Value: {doubleValue}</div>
<button
onClick={() => {
setDarkTheme(!darkTheme);
}}
>
Change Theme
</button>
</div>
);
}
#解決
使用 useMemo
useMemo
有點像快取,把複雜運算完的值記起來,就不用每次都重跑一次- 有兩個參數:
第一個:callback function
第二個:是一個陣列,像
useEffect
那樣,當陣列裡某元素的值改變時,就會執行第一個參數的 function
// 原本寫法
const doubleValue = slowFunction(value);
// 改為
const doubleValue = useMemo(() => {
return slowFunction(value);
}, [value]);
#注意
不可以在所有地方都使用 useMemo
useMemo
是用來優化效能的,過度使用反而會造成效能跟記憶體的額外消耗,原因說明:
useMemo
要判斷 dependency array 裡面元素的值是否有改變,若有的話才執行callback function,所以它在每次 render 都會執行,會造成不必要的性能消耗。useMemo
要記住 function 回傳的值,造成額外記憶體消耗。
#正確使用時機
- 像範例中想避免重複進行複雜耗時的計算時
- Referential Equality
簡單解釋什麼是 Referential Equality:物件型別擁有 call by reference 的特性,所以即使字面上看起來一樣,兩個物件其實是不一樣的。
const person1 = {
name: 'Ben'
}
const person2 = {
name: 'Ben'
}
console.log(person1 === person2) // -> false
若我們在範例中加上
useEffect(() => {
console.log("theme changed");
}, [themeStyle]);
在我們點擊按鈕改變 darkTheme
時, themeStyle
就會跟著改變,因此會印出 theme changed,這很好理解。
但是在改變 input 值的時候,也會印出 theme changed,這是因為每次 render 時,都會產生一個新的 themeStyle
物件,這個themeStyle
物件跟 render 前的 themeStyle
是不一樣的。
因此也可以使用 useMemo
來解決這個問題
// 原本
const themeStyle = {
color: darkTheme ? "#fff" : "#1b1b1b",
background: darkTheme ? "#1b1b1b" : "#fff"
};
// 改為
const themeStyle = useMemo(() => {
return {
color: darkTheme ? "#fff" : "#1b1b1b",
background: darkTheme ? "#1b1b1b" : "#fff"
};
}, [darkTheme]);