- Published on
「React筆記」PrevState in useState
- Authors
- Name
- Wen Chen
這算是搬到個人部落格後的第一篇文,但其實內容過去在 medium 就寫過了,不過最近剛好又在 debug 時遇到差不多的問題,於是重寫了一次算是複習一下。
useState
是ReactJs中非常使用到也幾乎是不可或缺的hook
之一,然而useState
的「非同步」性,卻是很常受到忽略的一部分,底下會分享近期開發上遇到的問題與筆記。
還記得過去初探 ReactJS 時,曾寫下這樣子的 Code :
const [show, setShow] = React.useState(false)
const handleClick = ()=>{
show ? setShow(false): setShow(true)
}
return (<div>
<button onClick={handleClick}>button</button>
</div>
);
想當然爾,上面那段 Code 肯定是壞了(否則也不會有這篇文)
上面那段程式碼,主要是要用state來控制某些元件的顯示與否,然而在實際測試之後,發現在點擊時卻會出現無法順利顯示/關閉的情況。
原因便在於 setState
在 update 時本身是非同步的,導致 update 時留下的微小「縫隙」,進而產生切換狀態時造成的無預期 bug。
而將程式碼修改成以下便恢復正常了:
const [show, setShow] = React.useState(false)
const handleClick = ()=>{
setShow(prev => !prev)
}
return (<div>
<button onClick={handleClick}>button</button>
</div>
);
React 官方文件中提到
如果新的 state 是用先前的 state 計算出,你可以傳遞一個 function 到 setState。該 function 將接收先前的 state,並回傳一個已更新的值。
另一個案例則是最近在工作上發現專案中某些緊鄰著 setState
做的行為產生的 bug ,我們先看下底下的程式碼,看結果之前可以先試著想一下 console 出來的結果會長怎樣:
const [state , setState] = React.useState(0)
React.useEffect(() => {
setState(1)
console.log("1 : " , state)
} , [])
console.log('2 : ' , state)
公佈答案:
// 2 : 0
// 1 : 0
// 2 : 1
上面只是簡化的示意版,然在專案中的使用情境主要是:
在接收到某些資料時,要改變畫面上的 state , 並且用新的資料去執行一次某個 function
而該 function 預設會用到該 state 去打 Api 獲取資料
把情境帶回去上面的程式碼後就可以發現,我們原先預期 useEffect
內的 function 會使用我們 setState
後的新值,然而事實並不如此,這個 effect
內所做的事情,總是會不如預期。
於是乎我們在程式碼中做了一點修改,把要執行的 function 加入可選 params,達到在沒有傳入參數時預設拿 state
來用; 在特定情境時,強制傳入參數來確保該 function
執行時資料的正確性。
實際上會有點像這樣:
const [state , setState] = React.useState(0)
const myFunction = ({ param = state } : { param? : number }) => {
console.log(param)
}
React.useEffect(() => {
// 符合某些條件時執行
setState(1)
myFunction({ param : 1 })
} , [])
當然真實專案會遠比範例還要複雜許多,也因為隨著複雜度的提升,我們常常會忽略掉這些最基本的細節。
而這些小細節往往就是出 bug 的關鍵,寫這篇同時也是謹惕自己。
唯有做好細節,才能寫出更完善的程式碼。