解讀useState第二個(gè)參數(shù)的"第二個(gè)參數(shù)"
場(chǎng)景引入
在學(xué)習(xí)react的過(guò)程中,為了希望能使用hook養(yǎng)成寫(xiě)函數(shù)式組件的習(xí)慣,我在完成日常作業(yè)的過(guò)程中刻意的使用hook,但也發(fā)現(xiàn)了幾個(gè)兩類(lèi)組件里需要注意的問(wèn)題。
先上一個(gè)需求場(chǎng)景:
當(dāng)我們?cè)陬?lèi)式組件中連續(xù)調(diào)用setState時(shí),因?yàn)閟etState方法是異步的,所以即使執(zhí)行到第三個(gè)setState方法時(shí),count仍為初始值0,所以其實(shí)執(zhí)行了三次setState,分別將count改為了1、2和3,但是因?yàn)樽詈笠粋€(gè)方法覆蓋了之前的state,所以count為3。
類(lèi)式組件
參數(shù)傳遞回調(diào)函數(shù)
在類(lèi)式組件中,如果我們希望使我們需要的三個(gè)setState方法都起我們想到的作用,最先想到的就是我們選擇setState中參數(shù)的另一種傳遞方式——回調(diào)函數(shù):
我們可以在此回調(diào)函數(shù)中收集到一個(gè)保存之前一次state的參數(shù),這樣雖然setState方法仍然是異步執(zhí)行,但是當(dāng)其執(zhí)行的時(shí)候每次都能取到上一次的state并在此基礎(chǔ)上做更新,可以達(dá)到我們想要的效果。
setState完成后執(zhí)行的回調(diào)函數(shù)
另一種方式是采用setState的第二個(gè)參數(shù)——state改變后的回調(diào)函數(shù):
這里要注意細(xì)節(jié)是不能提前解構(gòu)賦值,這樣只會(huì)取到第一次解構(gòu)出來(lái)的初始值0。
但是這樣的寫(xiě)法無(wú)疑是很難看的,寫(xiě)的多了就會(huì)形成回調(diào)地獄,使得代碼可讀性下降。
參數(shù)傳遞回調(diào)函數(shù)_promise版
為了解決回調(diào)地獄,很自然而然就想到使用promise做封裝:
將按鈕的回調(diào)函數(shù)變成異步函數(shù),并把每一個(gè)setState方法用promise封裝(因?yàn)閍wait關(guān)鍵字只能等待promise),然后我們?cè)趕tate更改成功的回調(diào)里使用resolve放行,以此來(lái)模擬一個(gè)同步的場(chǎng)景。
函數(shù)式組件
說(shuō)完了類(lèi)式組件,我們調(diào)過(guò)頭來(lái)使用hook來(lái)對(duì)我們的需求進(jìn)行重構(gòu)。
首先是使用useState的hook對(duì)組件進(jìn)行重構(gòu),當(dāng)然僅僅做改寫(xiě)肯定是沒(méi)辦法達(dá)到我們的需求的。
參數(shù)傳遞回調(diào)函數(shù)
于是立馬使用useState的第二個(gè)參數(shù),也就是操作數(shù)組的方法,將其寫(xiě)為一個(gè)回調(diào)函數(shù),目的和類(lèi)式組件一致,是為了拿到前一次的state:
那我們?nèi)匀缓茏匀坏木拖霃?fù)刻類(lèi)式組件中setState的第二個(gè)參數(shù)——也就是傳遞一個(gè)state成功修改的回調(diào)函數(shù)的方法,這樣我們的分享也可以到此結(jié)束了。
利用useEffect監(jiān)聽(tīng)count的變化
可惜useState中提供的修改state的方法并沒(méi)有提供這個(gè)回調(diào)函數(shù),意味著useState的第二個(gè)參數(shù)并沒(méi)有這么個(gè)“第二個(gè)參數(shù)”。
我們只好引入第二個(gè)hook:useEffect。
useEffect和類(lèi)式組件中的生命周期有關(guān)系但并非完全有關(guān)系,我們時(shí)常通過(guò)監(jiān)聽(tīng)一個(gè)空數(shù)組來(lái)模擬componnetDidMount生命周期,這里我們直接對(duì)count數(shù)據(jù)進(jìn)行監(jiān)聽(tīng)(類(lèi)似vue中的watcher方法)。
當(dāng)我們監(jiān)控到了count的改變后重新調(diào)用按鈕點(diǎn)擊回調(diào)事件,但是這也容易造成死循環(huán):回調(diào)函數(shù)改變數(shù)據(jù),數(shù)據(jù)修改再次調(diào)用回調(diào)。
所以我們要設(shè)置好跳出條件,我們?yōu)榱藢?shí)現(xiàn)+1+2+3的方法甚至不惜再使用一個(gè)useState,其實(shí)寫(xiě)到這里我們已經(jīng)覺(jué)得就實(shí)現(xiàn)這個(gè)需求而言已經(jīng)不是一個(gè)很好的方法了。
那我們是不是可以再用promise進(jìn)行包裝呢?
答案是可以!
async_await
這一塊其實(shí)是繞了一些彎子的,我們用promise進(jìn)行一層包裝后,發(fā)現(xiàn)沒(méi)有第二個(gè)參數(shù)供我們調(diào)用resolve,于是我們想到把resolve拋出,當(dāng)監(jiān)聽(tīng)到count的變化的時(shí)候再執(zhí)行resolve放行,同時(shí)把監(jiān)聽(tīng)到的count作為參數(shù)傳給下一個(gè)then方法,這個(gè)then方法再次返回一個(gè)新的promise并拋出一個(gè)新的resolve,當(dāng)然我們最后也可以打印一下函數(shù)看看我們綁定在函數(shù)上的這兩個(gè)resolve方法。
當(dāng)然還是不建議這么去做的,很明顯我們這種思路更多是為了強(qiáng)行去利用同步思想,在此作為一種思路擴(kuò)展。
總結(jié)
此處再提出一些思路供大家思考:
1、能否用setTimeout等方法包裹一下setState執(zhí)行順序呢?本質(zhì)思路其實(shí)是異步任務(wù)的執(zhí)行順序?qū)哟蔚乃伎剂恕?/p>
2、能否手寫(xiě)一個(gè)帶回調(diào)函數(shù)的useState呢?可以借助useRef。
這些僅為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
React Native中的RefreshContorl下拉刷新使用
本篇文章主要介紹了React Native中的RefreshContorl下拉刷新使用,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10React?Hook?Form?優(yōu)雅處理表單使用指南
這篇文章主要為大家介紹了React?Hook?Form?優(yōu)雅處理表單使用指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03react-redux action傳參及多個(gè)state處理的實(shí)現(xiàn)
本文主要介紹了react-redux action傳參及多個(gè)state處理的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07React中使用TS完成父組件調(diào)用子組件的操作方法
由于在項(xiàng)目開(kāi)發(fā)過(guò)程中,我們往往時(shí)需要調(diào)用子組件中的方法,這篇文章主要介紹了React中使用TS完成父組件調(diào)用子組件,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07react-redux中connect的裝飾器用法@connect詳解
這篇文章主要介紹了react-redux中connect的裝飾器用法@connect詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01每天學(xué)習(xí)一個(gè)hooks?useMount
這篇文章主要為大家介紹了每天學(xué)習(xí)一個(gè)hooks?useMount,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05Reactjs?+?Nodejs?+?Mongodb?實(shí)現(xiàn)文件上傳功能實(shí)例詳解
今天是使用?Reactjs?+?Nodejs?+?Mongodb?實(shí)現(xiàn)文件上傳功能,前端我們使用?Reactjs?+?Axios?來(lái)搭建前端上傳文件應(yīng)用,后端我們使用?Node.js?+?Express?+?Multer?+?Mongodb?來(lái)搭建后端上傳文件處理應(yīng)用,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2022-06-06