ItIron2023
react
真是沒完沒了是吧!昨天我們繼續看了一個不必要重複渲染的例子,了解到為什麼切context需要謹慎並再次的請出我們的React.memo。今天我們則會繼續歡迎這個老朋友,我不是江郎才盡,只是這玩意能帶出的錯誤實在是有夠多,馬上就開始吧!
請你看一下這個codesandbox以及下方的截圖。
今天我們在我們的App組件中有個ExpensiveChildComponent
組件(請假設它裡面有很多運算或邏輯造成渲染成本很高),其中它接受了來自App組件的setState函數作為props傳入,為了避免App組件的改動造成不必要的重新渲染,你將整個組件用React.memo包起來,但當點擊ExpensiveChildComponent
內的按鈕呼叫setCount函數時卻仍造成整個子組件重新渲染,請觀察以下的程式碼,試著解釋並修復這個問題。
const ExpensiveChildComponent = memo(({ onClick }) => {
// Plz assume this component is expensive somehow
console.log("ChildComponent re-rendered");
return <button onClick={onClick}>Click Me</button>;
});
export default function App() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
return (
<div>
<h1>React.memo not working corretly with function as props</h1>
<h2>Count: {count}</h2>
<ExpensiveChildComponent onClick={increment} />
</div>
);
首先我必須要先做個解釋,今天的題目看起來相當的不實際,畢竟正常來說你不會就這樣把setCount用另一個函數包裝後作為props傳進去,你大可以直接把setCount當作props傳進去,不過這足以展示我想說的問題了,這邊就稍微包容我一下吧!
我想如果你是從第一天就跟到現在的人,那你也許不知道怎麼修復這個問題,但你多半已經可以說明原因是什麼了,我們一再強調幾個概念,其中之一就是每一次的render都會重跑組件內的程式碼,包含變數與函數的宣告,再加上我們昨天也再次強調過React.memo僅做淺比對,以物件來說就是看reference是否有變動,現在仔細看一下程式碼。
const increment = () => setCount(count + 1);
<ExpensiveChildComponent onClick={increment} />
既然每一次的render都是新宣告的函數,reference自然也會是全新的,如此一來React.memo自然就再次判不上用場了,那麼你應該有方向我們該怎麼做了,我們要確保該函數有著相同的reference,除非必要,否則我們並不重新宣告這個函數。這就是useCallback出場的時候了,你要做的事情非常簡單,只需要將increment函數利用useCallback包起來並傳入空的dependency array就行了!
export default function App() {
const [count, setCount] = useState(0);
const increment = useCallback(() => setCount(prev => prev + 1), []); // 修改這邊即可
return (
<div>
<h1>React.memo not working corretly with function as props</h1>
<h2>Count: {count}</h2>
<ExpensiveChildComponent onClick={increment} />
</div>
);
}
是不是覺得這類的解決方式開始似曾相似了?那就表示你開始慢慢了解React是怎麼去處理這類的問題,這些方式基本的原理都相當類似,只是使用的對象有些不同罷了。
我們今天又一次的看了一個不必要渲染的問題,這個例子雖然很不實務但其實相當有意思,側面顯示了如果你並不了解他是怎麼運做的,很多時候你所謂的優化其實都是在做白工或扯後腿,務必評估你是否真的需要做這樣的優化、你的優化方式是否真的有效,我們這幾天談的React.memo, useMemo, useCallback適用情境都相當類似,絕大多數時候你都不需要把他們扯進來,再次強調,重新渲染是react很重要的一部分,它並不是什麼未知的猛獸需要被完全排除,你只要排除那些真正會造成不正確行為的即可! 關於這幾個小夥伴我們就這樣暫時告一段落了,在我們進入一些簡單的面試題目之前我們還會再有幾個新的情境,敬請期待!
本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!