ItIron2023
react
昨天我們用了一個非常基本的問題了解了關於react的render機制以及useState可能會碰到的一個小問題,這幾天我們會暫時離不開這個hook,馬上來看一下今天的問題吧!
順帶一提,我會盡可能地讓題目難度隨著時間遞增,所以如果你覺得這玩意真的他媽太簡單了,可以試著過幾天再回來追這系列文!
馬上就開始吧,請觀察這個codesandbox內的程式碼。
今天我們因為某種原因,期望點擊按鈕後一口氣呼叫5次的setCount讓count的值遞增5,但實際上的執行結果卻出乎我們意料,每一次的點擊都只讓count增加1,仿佛我們只寫了一個setCount一般,你是否能解釋並修復這個異常,讓點擊後順利的遞增我們setCount的次數呢?
const handleClick = () => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
};
看起來雖然是個簡單的useState問題,但也許答案並不是你想得那樣,我之前在碰到人向我提問這類問題時,他們一個很常見的誤解就是以為這是因為setState是非同步行為,實際上問題並沒有這麼玄妙,setState也100%是個同步的操作,僅是你對react渲染仍不夠瞭解而已!
最根本就根本的原因其實在於state在每一次的render其實都是維持相同的值(簡單說是因為在closure內),什麼? 你聽不懂? 加上log我想你就會清楚一些了。
const handleClick = () => {
setCount(count + 1);
console.log('count1', count) // count1 0
setCount(count + 1);
console.log('count2', count) // count2 0
setCount(count + 1);
console.log('count3', count) // count3 0
setCount(count + 1);
console.log('count4', count) // count4 0
setCount(count + 1);
console.log('count5', count) // count5 0
};
注意到了嗎? 雖然你call setState了,但當下印出每次的值都是初始值0,這下你理解了,實際上上方的程式碼與下方的等價
const handleClick = () => {
setCount(0 + 1);
setCount(0 + 1);
setCount(0 + 1);
setCount(0 + 1);
setCount(0 + 1);
};
在下次render前,你count的值都不會有任何變動,值本身是immutable的。 既然當下的值是0,你每次去設0+1自然結果都會是1囉!了解原因後我想你就比較好辦了,我們要做的就是利用前一次的count值當基礎再加上1就行了,因此改為callback的形式就會一切正常囉!
const handleClick = () => {
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
};
在這樣的情況下,每一次都會以前一次的count值作為基礎再加上1,另一方面react也會batch這些setState的操作,讓他們能在下一次的rerender前完成,最終就是一口氣看到5囉!
今天我們稍微更深入的探討了react的render行為以及常見的誤解,在初學看來也許會覺得react有些不合理(事實也的確如此),但這就是react底層的運作機制,隨著題目繼續進行下去相信你對於整個渲染行為會有更深的理解,未來遇到類似的問題也會較為容易切入,我們明天見!
本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!
因為我才剛接觸react約1週,可能理解上有問題
主要是對你說setState是同步的這點有所疑問?
function handleInc() {
console.log(likes); // 0
setLikes(likes + 1);
console.log(likes); // 0
setLikes((likes) => likes + 1);
console.log(likes); // 0
}
blank 非常棒的問題!有多少剛接觸react的人有辦法講出fiber tree這玩意,看得出你很用心在學,先給你個讚!我分別回答你兩個問題,我們先看第一個吧。
setState怎麼會是同步的?
正確說來是呼叫setState更新狀態這件事情是同步的,但因為react本身的一些優化機制(例如batch update)讓它看起來像是非同步操作,而狀態更新與組件的渲染行為就是100%的非同步操作。
這是個大家已經爭論已久的問題,也是在面試時被問到有可能會吵架的東西,慢慢的有些人變得不是這麼在意這些細微的差異,而是把關注放在最終的執行結果上,我這邊礙於篇幅沒辦法跟你說明得太過於詳細,你有興趣的話可以用react setState synchronous之類的關鍵字搜尋看看,繁體中文針對這點的討論真的不算太多,很多人就是把它當作完全的非同步就這樣過去了。
要去哪裡看這些底層的東西?
我的話會看一些文章分析的參考來源,其中不免就會提到react本身的repo可以翻原始碼,或是有些youtube影片也會做這類的主題探討,我記得有一部是叫Deep dive to react的系列
你有興趣的話可以用react setState synchronous之類的關鍵字搜尋看看
我後來看這篇 所以我可以理解呼叫setState更新狀態這件事情是同步
的這一個事實,而後續的狀態更新與組件渲染是非同步的,那這樣就是代表setState內部有類似非同步的callback function嗎?
我記得有一部是叫Deep dive to react的系列
了解了~我覺得身為非本科轉職的junior,在認真弄清楚語法的底層邏輯還有學習新框架這點真的會很糾結。有點像是無限迴圈的感覺,學新的框架慢就是因為對於這些基本觀念還不夠了解,但是在學這些基本觀念時,又會有種焦躁感。畢竟學習過的成就感跟學框架後做一些東西的成就感還是有落差XD,前者就像是非同步,要累積一段時間才會整個湧現,後者就是很直觀的同步。有去看一下你的部落格,看完這篇後覺得收穫很多~