第一個(gè)可以在條件語(yǔ)句中使用的原生Hook誕生了

大家好,我卡頌。
在10月13日的first-class-support-for-promises RFC[1]中,介紹了一種新的hook? —— use。
use?什么?就是use?,這個(gè)hook?就叫use。這也是第一個(gè):
- 可以在條件語(yǔ)句中書寫的hook
 - 可以在其他hook回調(diào)中書寫的hook
 
本文來(lái)聊聊這個(gè)特殊的hook。
use是什么
我們知道,async函數(shù)會(huì)配合await關(guān)鍵詞使用,比如:
類似的,在React組件中,可以配合use起到類似的效果,比如:
可以認(rèn)為,use的作用類似于:
- async await?中的await。
 - generator?中的yield。
 
use?作為「讀取異步數(shù)據(jù)的原語(yǔ)」,可以配合Suspense實(shí)現(xiàn)「數(shù)據(jù)請(qǐng)求、加載、返回」的邏輯。
舉個(gè)例子,下述例子中,當(dāng)fetchNote?執(zhí)行異步請(qǐng)求時(shí),會(huì)由包裹Note?的Suspense組件渲染「加載中狀態(tài)」。
當(dāng)請(qǐng)求成功時(shí),會(huì)重新渲染,此時(shí)note數(shù)據(jù)會(huì)正常返回。
當(dāng)請(qǐng)求失敗時(shí),會(huì)由包裹Note的ErrorBoundary組件處理失敗邏輯。
其背后的實(shí)現(xiàn)原理并不復(fù)雜:
- 當(dāng)Note組件首次render,fetchNote發(fā)起請(qǐng)求,會(huì)throw promise,打斷render流程。
 - 以Suspense fallback作為渲染結(jié)果。
 - 當(dāng)promise狀態(tài)變化后重新觸發(fā)渲染。
 - 根據(jù)note的返回值渲染。
 
實(shí)際上這套「基于promise的打斷、重新渲染流程」當(dāng)前已經(jīng)存在了。use的存在就是為了替換上述流程。
與當(dāng)前React?中已經(jīng)存在的上述「promise流程」不同,use?僅僅是個(gè)「原語(yǔ)」(primitives),并不是完整的處理流程。
比如,use并沒有「緩存promise」的能力。
舉個(gè)例子,在下面代碼中fetchTodo?執(zhí)行后會(huì)返回一個(gè)promise,use?會(huì)消費(fèi)這個(gè)promise。
當(dāng)Todo組件的id prop變化后,觸發(fā)fetchTodo重新請(qǐng)求是符合邏輯的。
但是當(dāng)isSelected prop變化后,Todo組件也會(huì)重新render,fetchTodo執(zhí)行后會(huì)返回一個(gè)新的promise。
返回新的promise不一定產(chǎn)生新的請(qǐng)求(取決于fetchTodo的實(shí)現(xiàn)),但一定會(huì)影響React接下來(lái)的運(yùn)行流程(比如不能命中性能優(yōu)化)。
這時(shí)候,需要配合React提供的cache API(同樣處于RFC)。
下述代碼中,如果id prop不變,fetchTodo始終返回同一個(gè)promise:
use的潛在作用
當(dāng)前,use的應(yīng)用場(chǎng)景局限在「包裹promise」。
但是未來(lái),use會(huì)作為客戶端中處理異步數(shù)據(jù)的主要手段,比如:
- 處理context
 
use(Context)?能達(dá)到與useContext(Context)?一樣的效果,區(qū)別在于前者可以在條件語(yǔ)句,以及其他hook回調(diào)內(nèi)執(zhí)行。
- 處理state
 
可以利用use實(shí)現(xiàn)新的原生狀態(tài)管理方案:
為什么不使用async await
本文開篇提到,use原語(yǔ)類似async await中的await,那為什么不直接使用async await呢?類似下面這樣:
有兩方面原因。
一方面,async await的工作方式與React客戶端處理異步時(shí)的邏輯不太一樣。
當(dāng)await的請(qǐng)求resolve后,調(diào)用棧是從await語(yǔ)句繼續(xù)執(zhí)行的(generator中yield也是這樣)。
而在React中,更新流程是從根組件開始的,所以當(dāng)數(shù)據(jù)返回后,更新流程是從根組件從頭開始的。
改用async await的方式勢(shì)必對(duì)當(dāng)前React底層架構(gòu)帶來(lái)挑戰(zhàn)。最起碼,會(huì)對(duì)性能優(yōu)化產(chǎn)生不小的影響。
另一方面,async await這種方式接下來(lái)會(huì)在Server Component中實(shí)現(xiàn),也就是異步的服務(wù)端組件。
服務(wù)端組件與客戶端組件都是React組件,但前者在服務(wù)端渲染(SSR),后者在客戶端渲染(CSR),如果都用async await,不太容易從代碼層面區(qū)分兩者。
總結(jié)
use?是一個(gè)「讀取異步數(shù)據(jù)的原語(yǔ)」,他的出現(xiàn)是為了規(guī)范React在客戶端處理異步數(shù)據(jù)的方式。
既然是原語(yǔ),那么他的功能就很底層,比如不包括請(qǐng)求的緩存功能(由cache處理)。
之所以這么設(shè)計(jì),是因?yàn)镽eact?團(tuán)隊(duì)并不希望開發(fā)者直接使用他們。這些原語(yǔ)的受眾是React生態(tài)中的其他庫(kù)。
比如,類似SWR?、React-Query?這樣的請(qǐng)求庫(kù),就可以結(jié)合use?,再結(jié)合自己實(shí)現(xiàn)的請(qǐng)求緩存策略(而不是使用React?提供的cache方法)
各種狀態(tài)管理庫(kù),也可以將use作為其底層狀態(tài)單元的容器。
值得吐槽的是,Hooks?文檔中hook的限制那一節(jié)恐怕得重寫了。
參考資料
[1]first-class-support-for-promises RFC:https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md。















 
 
 





 
 
 
 