React 18的新hook,useSyncExternalStore分析
在react18之后,很多狀態(tài)管理庫,都使用了useSyncExternalStore這個(gè)庫去適應(yīng)react18的concurrent features,雖然concurrent在之前的版本中就已經(jīng)提供,但是默認(rèn)并不開啟。
在說明useSyncExternalStore之前,我們先來看一下redux相關(guān)的一些信息。
redux是一個(gè)狀態(tài)管理工具,它可以運(yùn)行在react、vue等庫中,在react中使用,通常需要使用react-redux讓redux和react連接起來,而現(xiàn)在流行的redux-toolkit則是一個(gè)讓我們更加方便的可以創(chuàng)建store、action、reducer的一個(gè)工具。
redux的設(shè)計(jì)理念其實(shí)一直沒有變化,但是隨著react的不斷更新,react-redux就需要不斷的做出調(diào)整。
那么react-redux都有哪些改變呢?
最初react-redux其實(shí)相當(dāng)簡(jiǎn)單,我們使用它提供的connect函數(shù)將redux和react連接起來,其實(shí)connect其實(shí)就是一個(gè)高階組件,因?yàn)閔ooks沒有流行起來之前,class組件是主流,所以這種裝飾器寫法相當(dāng)簡(jiǎn)潔。我們暫且將傳入connect的組件稱為子組件,將connect創(chuàng)建的組件成為稱為父組件。
connect的第一個(gè)參數(shù)名稱mapStateToProps,其實(shí)就是這個(gè)函數(shù)的作用,根據(jù)傳入的對(duì)象在父組件中創(chuàng)建對(duì)應(yīng)的state,對(duì)store進(jìn)行訂閱,然后再將state通過props傳遞到傳入的子組件中使用。那么這個(gè)時(shí)候調(diào)用dispatch更改store數(shù)據(jù)之后,就會(huì)觸發(fā)訂閱,調(diào)用setState,相應(yīng)的子組件也會(huì)更新。
后來,hooks出現(xiàn)之后,函數(shù)式組件成為主流,class組件基本銷聲匿跡。react-redux順應(yīng)潮流,接著提供了useSelector、useDispatch等hook,以便我們?cè)诤瘮?shù)式組件中使用。
useSelector其實(shí)也比較簡(jiǎn)單,它根據(jù)傳入的selector返回store中的值,同時(shí)也會(huì)去訂閱store,等到store數(shù)據(jù)變化,則調(diào)用forceRender觸發(fā)更新。
這種發(fā)布訂閱以及觸發(fā)更新的模式,和antd中form基本一樣。
而到了react18中,我們?cè)倏磖eact-redux中useSelector的實(shí)現(xiàn),我們?nèi)绻哑渲写a精簡(jiǎn)一下,則變成了下面這樣
所有的內(nèi)在邏輯其實(shí)都放到了useSyncExternalStore中,當(dāng)成一個(gè)react的api提供給其他狀態(tài)管理庫的開發(fā)者使用。我們?nèi)绻製seSyncExternalStore的實(shí)現(xiàn)代碼刪減一下,就能發(fā)現(xiàn)和上面react-redux里面的代碼邏輯非常像,也是訂閱、然后再store變化時(shí)forceUpdate,其實(shí)都是通過useState觸發(fā)更新。
很多文章里面都講useSyncExternalStore是為了解決react18中concurrent特性引發(fā)的UI撕裂問題,但是本質(zhì)上,這個(gè)hook其實(shí)就是對(duì)使用外部數(shù)據(jù)時(shí)的邏輯內(nèi)聚,我們只需要在使用時(shí)提供給react一個(gè)讀取store的方法getSnapshot,一個(gè)用于訂閱的方法subscribe,我們就可以在store的更新后自己去控制觸發(fā)react更新。react在調(diào)用我們提供給它的subscribe函數(shù)時(shí),傳入的callback,其實(shí)就是一個(gè)forceUpdate。
上面的例子,本質(zhì)上和下面是一樣的