爲何react-redux沒有更新hooks API

按照正常來說,像 React-Redux 這一類較爲活躍的社區類庫,在 React 有較大的更新出現的時候通常都會及時跟進的。而這一次 React 的 Hooks 發佈,有將近兩個月的 beta 期,以及到截止本篇文章發佈已經 Hooks 正式版也已經將近一個月來,React-Redux 到如今都沒有正式發佈一個相似useRedux這樣的 Hooks API,那麼這是爲何呢?咱們來分析一下緣由。react

去年年末,出於興趣,研究了一波 redux 和 react-redux 的源碼,除了在原理上的理解以外,讓我較爲好奇的一點就是:React-Redux到目前爲止都沒有對 Hooks 進行支持。從使用角度上來說,出現一個相似:git

function ConnectedComponent() {
  const store = useReduxStore()
  const state = useRedux(function mapState()) } 複製代碼

這樣的代碼是很是能夠理解的,並且也是很是符合 Hooks 的使用習慣的,事實上社區上也出現來不少非官方的 reudx Hooks 的類庫:github

說明了整體上社區對於 Hooks 的接受度是很高的,你們應該都在期待官方能給出一個真正的 Hooks API。那麼爲何 React-Redux 到如今都沒有發佈正式的 Hooks API 呢?redux

在翻閱 React-Redux 的 issues 列表的時候,我發現了這個 issue。做者很是完整得爲咱們介紹了 React-Redux 從最初得 idea 到如今 v6 版本得成長曆程。那麼 v6 版本相比 v5 版本有哪些大的變化呢?緩存

  • 使用createContext來傳遞 state
  • 只有Provider訂閱了 store 的變化
  • 再也不對被connect的組件傳遞 store 對象

v6 版本更新這些內容的主要緣由以下:app

  • 老的 context API 即將被刪除,而且若是和新的 context API 一塊兒使用會有問題
  • React 即將推出Concurrent Mode異步渲染,若是使用老的方式可能會致使不一樣的子樹獲取的狀態不一樣,使用新的 context API,React 會確保整棵樹拿到的是相同的狀態
  • createContext默認帶有top-down數據流,再也不須要 React-Redux 本身實現

以上是 v6 版本的變化和其緣由,可是到目前爲止咱們好像並無看到任何說起 Hooks 的地方。別急,接下去就是正題了。異步

在升級到 v6 的過程當中,React-Redux 團隊發現 v6 版本的總體性能是比不上 v5 的。這個性能降低的主要緣由不是 React-Redux 的實現代碼有什麼問題,其主要問題是來自createContent的實現方式,以及 React-Redux 選擇了只有在Provider中訂閱 store 變化。ide

注意性能

React-Redux 選擇使用createContext和只有在Provider中訂閱都是沒有任何問題的,也是 React 官方推薦的使用方法,從面向將來的眼光來看,這是勢必的升級。因此同窗們在後面分析問題的時候不要問,爲何不換個實現方式啥的。測試

那麼所謂的性能問題具體是怎麼來的呢?**主要緣由是createContext在 value 變化的時候他是如何通知子樹的。**咱們先來看一組性能測試對比圖:

react-redux-benchmark

這個測試用例來自react-redux-benchmarks,你們有興趣能夠本身去跑一下。

從圖中咱們能夠看出來,v6 主要性能下降的點是來自於Scripting,也就是運行 JavaScript 腳本的時間,從數據上來看,是 v5 版本的兩倍多。雖然在RenderingPainting階段要好不少,可是由於Scripting的佔比最大,因此整體上講是略微有些降低的。

**其根本緣由是createContext的實現方式中,咱們更改了Provider的 value,那麼在此次更新週期中,React 會遍歷Provider的全部子節點,並對監聽了這一個 context 的節點進行標記,讓後續渲染中知道這個節點是須要更新的,即使他的 props 和 state 根本沒有變化。**關於爲何 React 要這麼去實現的緣由不是一句話能講完的,他涉及到 React 16 以後 Fiber 判斷一個節點是否有更新的方法,後面我會單獨寫一篇文章來說解,如今你們只須要知道他就是這麼實現的就能夠了。

由於上訴的緣由,咱們能夠想象在一個節點很是多的 React 應用中,一個相似 React-Redux 這樣放置在最頂層的Provier數據變化以後,他的整體計算量確定是很是大的。

相對的,在 v5 中由於使用老的 context API,爲了不一些這個 API 帶來的問題,因此 React-Redux 團隊選擇在connect返回的WrapperComponent HOC 中進行 store 數據變化的監聽,也就是說 Store 變化以後以後被connect的組件可能出現 props 上的變化,而沒有任何須要遍歷子樹的須要。

以上就是 v6 版本在性能上不升反降的緣由。這也是 React 新的 context API 不是很適合用在變化頻繁的數據上緣由。咱們能夠想象若是咱們像之前同樣把一個表單的全部項數據都緩存在 redux store 裏面,每次輸入都要更新 store,可能帶來的對總體性能的影響。關於這一塊,React 也有一個issue在討論是否以及如何設計一個方案來解決這個性能上的問題。這個討論很是熱鬧,你們有興趣能夠關注一下。

那麼到如今爲止咱們仍是沒有講到 Hooks 相關的任何內容,是否是有點偏題了?不,由於咱們已經知道了大部分的緣由,那就是新的 context API 存在的性能問題。而這個問題,反應到 Hooks 上面,則會更大程度地體現出來。

若是咱們要封裝一個相似useRedux這樣的 Hook,那麼咱們確定須要用到useContext來獲取Provider提供的 state,畢竟Provider是惟一訂閱來 store 變化的。而使用了useContext,就表明咱們這個組件是依賴於這個 context 的,也就是說一旦 state 變化,這個組件就會被標記爲須要更新

而按照咱們一直以來的使用 React-Reudx 的狀況,咱們都會提供mapState來映射組件真正須要監聽的數據,由於 store 是整個應用的,不太會存在某一個組件須要整個應用全部的數據的狀況。這種狀況下,在 v5 版本中,甚至是在 v6 版本中使用connect的狀況,都會在 HOC 中進行mapState的執行進行數據映射,而後經過shallowEqual判斷是否有依賴的 state 變化,若是沒有實際上是不須要更新真正的組件的。

可是在使用useContext的狀況,即使咱們給useRedux提供來mapState,可是他的執行依然要等到這個組件真正開始執行更新的時候。也就是說咱們沒法讓 React 在更新這個組件以前就判斷他是否能夠不被更新,那麼 React 提供的優化就沒啥用了。

而同時一旦咱們的組件開始執行,即使咱們發現useRedux返回的 map 以後的 state 其實跟上一次是同樣的,咱們也沒法告訴 React 這個組件實際上是不須要更新的來終止此次更新。因此,這是一個沒法在**類庫層面進行的優化。**要優化咱們只有經過使用者本身使用useMemo這樣的 API,那麼對於開發經驗不是那麼多的同窗,極可能會致使這個組件會被頻繁進行無用更新,而致使性能浪費。

那麼以上就是爲何 React-Redux 以及不少經常使用類庫尚未更新 Hooks API 的緣由來,目前來講這個性能問題較爲無解,React 官方也在考慮是否要出一些新的 context 相關的 API 來專門優化更新頻率較高的狀況,咱們也只能拭目以待了。

目前來講,若是你不清楚createContent的這些問題,建議不要把常常須要更新的內容放在 context 裏面(除非沒有別的方法)。

以上,就是我對於爲何 Hooks 如今呼聲這麼高,可是社區支持卻沒有這麼快跟進的緣由分析,若是有任何問題,能夠直接回復郵件,或者在個人AMA(Ask Me Anything)中給我提問,我都會瀏覽,而且進行解答。

另外在這裏提出的一些問題,也會在後續進行更詳細的解析:

  • createContext 爲何在更新的時候要遍歷全部子樹節點
  • React 中判斷一個節點是否能夠跳過更新的判斷條件

我是Jocky,若是對於我分析的React內容感興趣,能夠訂閱我,我會保持對React生態更新及時跟進,以及對React及其生態的內容進行深度解析。

相關文章
相關標籤/搜索