PLC程序解鎖解碼【電;I7I54833762】方法和思路分享,首先是設(shè)置 subscription 的 onStateChange(它初始是個(gè)空方法,需要注入實(shí)現(xiàn)),它會(huì)在觸發(fā)更新時(shí)調(diào)用,它這里希望將來調(diào)用的是subscription.notifyNestedSubs,subscription.notifyNestedSubs會(huì)觸發(fā)這個(gè) subscription 收集的所有子訂閱。也就是說這里的更新回調(diào)和『更新』沒有直接關(guān)系,而是觸發(fā)子節(jié)點(diǎn)們的更新方法。
然后調(diào)用了subscription.trySubscribe(),它會(huì)將自己的 onStateChange 交給父級(jí) subscription 或者 redux 去訂閱,將來由它們觸發(fā) onStateChange
最后它會(huì)判斷之前的 state 和最新的是否一致,如果不一致會(huì)調(diào)用subscription.notifyNestedSubs(),它會(huì)觸發(fā)這個(gè) subscription 收集的所有子訂閱從而更新它們。
返回了注銷相關(guān)的函數(shù),它會(huì)注銷在父級(jí)的訂閱,將subscription.onStateChange重新置為空方法。這個(gè)函數(shù)會(huì)在組件卸載或 re-render (僅 store 變化時(shí))時(shí)被調(diào)用(react useEffect 的特性)。
Provider 有很多地方都涉及到了 subscription,subscription 的那些方法只是講了大概功能,關(guān)于 subscription 的細(xì)節(jié)會(huì)在后面 subscription 的部分講到。
這里會(huì)講到 Provider 中出鏡率很高的 subscription 部分,它是 react-redux 能夠嵌套收集訂閱的關(guān)鍵。其實(shí)這個(gè)部分的標(biāo)題叫做 Subscription 已經(jīng)不太合適了,在 8.0.0 版本之前,react-redux 確實(shí)是通過 Subscription class 實(shí)現(xiàn)它的,你可以通過new Subscription()使用創(chuàng)建 subscription 實(shí)例。但在 8.0.0 之后,已經(jīng)變成了createSubscription函數(shù)創(chuàng)建 subscription 對(duì)象,內(nèi)部用閉包替代原先的屬性。
用函數(shù)替代 class 有一個(gè)好處是,不需要關(guān)心 this 的指向,函數(shù)返回的方法修改的永遠(yuǎn)是內(nèi)部的閉包,不會(huì)出現(xiàn) class 方法被賦值給其他變量后出現(xiàn) this 指向變化的問題,降低了開發(fā)時(shí)的心智負(fù)擔(dān)。閉包也更加私有化,增加了變量安全。同時(shí)在一個(gè)支持 hooks 的庫(kù)里,用函數(shù)實(shí)現(xiàn)也更符合開發(fā)范式。
addNestedSub非常巧妙的運(yùn)用了遞歸,它里面又調(diào)用了trySubscribe。于是它們就會(huì)達(dá)到這樣的目的,當(dāng)最底層subscription發(fā)起trySubscribe想被父級(jí)收集訂閱時(shí),它會(huì)首先觸發(fā)父級(jí)的trySubscribe并繼續(xù)遞歸直到根subscription,如果我們把這樣的層級(jí)結(jié)構(gòu)想象成樹的話(其實(shí) subscription.trySubscribe 也確實(shí)發(fā)生在組件樹中),那么就相當(dāng)于從根節(jié)點(diǎn)到葉子節(jié)點(diǎn)依次會(huì)被父級(jí)收集訂閱。因?yàn)檫@是由葉子節(jié)點(diǎn)先發(fā)起的,這時(shí)除了葉子節(jié)點(diǎn),其他節(jié)點(diǎn)的訂閱回調(diào)還沒有被設(shè)置,所以才設(shè)計(jì)了handleChangeWrapper這個(gè)回調(diào)外殼,注冊(cè)的只是這個(gè)回調(diào)外殼,在將來非葉子節(jié)點(diǎn)設(shè)置好回調(diào)后,能被外殼觸發(fā)。
在『遞』過程結(jié)束后,從根節(jié)點(diǎn)開始到這個(gè)葉子節(jié)點(diǎn)的訂閱回調(diào)handleChangeWrapper都正在被父級(jí)收集了,『歸』的過程回溯做它的本職工作return listeners.subscribe(listener),將子subscription的訂閱回調(diào)收集到收集器listeners中(將來更新發(fā)生時(shí)會(huì)觸發(fā)相關(guān)的handleChangeWrapper,而它會(huì)間接的調(diào)用收集到所有的 listener)。
所以每個(gè)subscription的addNestedSub都做了兩件事:1. 讓自己的訂閱回調(diào)先被父級(jí)收集;2. 收集子subscription的訂閱回調(diào)。
結(jié)合addNestedSub的解釋再回過頭來看trySubscribe,它想讓自己的訂閱回調(diào)被父級(jí)收集,于是當(dāng)它被傳入父級(jí)subscription時(shí),就會(huì)調(diào)用它的addNestedSub,這會(huì)導(dǎo)致從根subscription開始每一層subscription都被父級(jí)收集了回調(diào),于是每個(gè)subscription都嵌套收集了它們子subscription,從而父級(jí)更新后子級(jí)才更新成為了可能。同時(shí),因?yàn)?code>unsubscribe這個(gè)鎖的存在,如果某個(gè)父級(jí)subscription的trySubscribe被調(diào)用了,并不會(huì)重復(fù)的觸發(fā)這個(gè)『嵌套注冊(cè)』。
useIsomorphicLayoutEffectWithArgs是一個(gè)工具函數(shù),內(nèi)部是useIsomorphicLayoutEffect,這個(gè)函數(shù)前面也講過。它們最終做的是:將第 2 個(gè)數(shù)組參數(shù)的每項(xiàng)作為參數(shù)給第一個(gè)參數(shù)調(diào)用,第 3 個(gè)參數(shù)是useIsomorphicLayoutEffect的緩存依賴。
被執(zhí)行的第一個(gè)參數(shù)captureWrapperProps,它主要功能是判斷如果是來自 store 的更新,則在更新完成后(比如 useEffect)觸發(fā)subscription.notifyNestedSubs,通知子訂閱更新。
接著它想生成actualChildProps,也就是 select 出來的業(yè)務(wù)組件需要的 props,其中主要使用了useSyncExternalStore,如果你追到useSyncExternalStore的代碼里看,會(huì)發(fā)現(xiàn)它是一個(gè)空方法,直接調(diào)用會(huì)拋出錯(cuò)誤,所以它是由外部注入的。在入口index.ts里,initializeConnect(useSyncExternalStore)對(duì)它進(jìn)行初始化了,useSyncExternalStore來自 React 。所以actualChildProps實(shí)際是React.useSyncExternalStore( subscribeForReact, actualChildPropsSelector, getServerState ? () => childPropsSelector(getServerState(), wrapperProps) : actualChildPropsSelector)的結(jié)果。
useSyncExternalStore是 react18 的新 API,前身是useMutableSource,為了防止在 concurrent 模式下,任務(wù)中斷后第三方 store 被修改,恢復(fù)任務(wù)時(shí)出現(xiàn)tearing從而數(shù)據(jù)不一致。外部 store 的更新可以通過它引起組件的更新。在react-redux8之前,是由useReducer手動(dòng)實(shí)現(xiàn)的,這是react-redux8首次使用新 API。這也意味著你必須跟著使用 React18+。但我認(rèn)為其實(shí) react-redux8 可以用 shim: import { useSyncExternalStore } from 'use-syncexternal-store/shim';來做到向下兼容。
審核編輯:符乾江
電子發(fā)燒友App





























































評(píng)論