[譯] ⚛ React 狀態管理工具博物館

⚡️最熱門的 React 狀態管理庫瞭解一下

爲何?

這篇文章是瞭解複雜的狀態管理系統的羅塞塔石碑(關鍵所在)。一個打包列表應用中使用的狀態管理庫基本以下:html

固然,你可能對上面的某些庫很熟悉,如今你能夠運用這些知識來更好地理解其它庫。你不只有機會來領會這些庫的細節,還會發現這些庫是多麼類似。前端

爲了通俗易懂地說明這些庫,我選擇了一個只有添加清空功能的簡單打包列表應用做爲示例。react

很容易獲取到應用 (Native 和 Web 都已實現)。android

爲了說明狀態如何流轉,全部的示例中,添加/清空功能是一個組件,列表功能是另外一個組件。ios

兩個主要的組件(添加/列表)被抽象爲一個須要導入的庫,只留下基本代碼來強調狀態的選擇。代碼力求簡約。git

歡迎來到 React 狀態管理工具博物館!

使用每一個狀態庫實現的 React 和 React Native 應用的源碼均可以在下面的倉庫中找到。github

github.com/GantMan/Rea…redux

使用上面的倉庫親自進入這些狀態庫中查看它們吧!🔥後端


每種解決方案的我的筆記:

若是你想看代碼,能夠去查看 GitHub 源碼,若是你須要建議,請繼續閱讀下面這段很長的描述。安全

這裏咱們會探討博物館中每一個狀態庫的差別,正是這些差別讓每一個都不同凡響。若是你有好的建議或經驗,請在評論中與你們分享。我也有興趣把這篇文章看成充滿樂趣的會議進行討論。

setState

這是狀態管理最基礎的結構,僅基於對組件及其封裝的理解。從不少方面來看,這對 React 初學者來講是一個很好的例子。顯式地將狀態提高到根組件中,全部組件都是根組件的子組件,其中定義了 props 與 state 的關係。隨着應用的增加,到子組件的顯式鏈接代碼愈來愈複雜、脆弱,這就是不常用這種方法的緣由。

源碼:React | React Native

React Context

reactjs.org/docs/contex…

關於 Context 的更新引起了不少關注。實際上,在 16.x 的版本中 Context 的最終形態有點像狀態管理系統自己。簡而言之,context 設置了 providerconsumer。一個 provider 的全部子組件均可以訪問其中應用的值。全部非子組件都會看到 context 的默認值。下圖解釋了這種關係。

只有子組件才能繼承。

另外一個很是重要的觀點,我並不喜歡 Consumer 的語法結構。顯而易見,這是 Consumer 中的一個函數,但它在這種狀況下超負荷使用過多大括號,彷佛違背了 JSX 的原則。

<PackingContext.Consumer>
        {({newItemName, addItem, setNewItemName, clear}) => (
          <AddPackingItem addItem={addItem} setNewItemText={setNewItemName} value={newItemName} clear={clear} /> )} </PackingContext.Consumer> 複製代碼

一個迂腐的問題,但 API 的設計總應該考慮到代碼的可讀性,在這方面,Context 有點不整潔。

源碼:React | React Native

Redux

github.com/reactjs/rea…

在寫這篇文章的時候,我敢說 Redux 是最受歡迎的狀態管理工具,所以 Redux 受到的攻擊也最多。使用 Redux 解決狀態管理問題須要寫不少文件,代碼量幾乎翻倍。但 Redux 也有優勢,它簡單而靈活。

若是你不熟悉 Redux,這是一種狀態管理的方法,它以 reducer 函數的形式提供時間旅行和狀態清理功能。Dan Abramov 講解 redux 的視頻已經被觀看過不少次。

  • YouTube 視頻連接:https://youtu.be/xsSnOQynTHs

簡而言之,就像有人在你的應用中發出命令(Actions),這些命令是經過 Action Creators 建立出來的。你的應用中的數據管理器(Reducers)能夠聽到這些留言,並能夠選擇對其進行操做。我喜歡個人海盜船比喻,因此大聲喊叫『有人落水』能夠告訴你的船員們若是他們反駁,那就會少一位船員,會計師從新分配寶藏,擦拭甲板的人能夠忽略它,由於他不在意。

我喜歡這個比喻,由於『大聲呼喊』是管理應用每一個角落的一種強大方式,特別是大型複雜應用。結合這種方式沒法處理異步而且須要粘在一個不可變結構上才能所有工做,Redux 是按時計薪的開發者的朋友。

源碼:React | React Native

MobX

github.com/mobxjs/mobx…

MobX 是上手最簡單的狀態管理庫之一。查看它的 README 文件,而後按照步驟進行操做,立刻就能夠運行了。這感受像是可變的 JavaScript,在某種程度上確實是。惟一可能會讓你感到迷惑的是在類中使用的像 @observer 這些裝飾器函數。雖然這種寫法有點奇怪,但會讓代碼更簡潔。

若是你使用過 redux 的東西,@observer 就像把 mapStateToProps 方法和 reselect 方法自動組合同樣 — Steve Kellock

若是想了解更多關於切換到 MobX 的進階話題,請查看下面 Nader 的文章。

總之,MobX 是最小、最簡單的工具之一!

源碼:React | React Native

Unstated

github.com/jamiebuilds…

Unstated 這個庫和 MobX 同樣簡單。和 MobX 同樣,這個感受也是可變的 JavaScript,看上去使用 Unstated 須要添加更多的 React 代碼。我實際上以爲 Unstated 比 Context 更像是 React。

使用起來很簡單,建立一個 container 組件,就在這個組件內部管理狀態。像 setState 這樣簡單的已知函數已經內置在這個狀態容器中了。"Unstated" 不只只是一個貼切的名字,並且確實是一個精巧的基於 React 的狀態管理工具。

我不清楚它如何擴展或處理中間件等。可是若是你是狀態管理的初學者,MobX 和 Unstated 都是上手最簡單的選擇!

源碼:React | React Native

MobX-State-Tree

github.com/mobxjs/mobx…

是的,這個庫常常會被人誤會和 MobX 有什麼關係,其實它們倆很是不一樣。

甚至個人同事也將它的名字縮短爲 MobX,但我老是推薦 MST 做爲其簡稱。正由於如此,重要的是 MobX-State-Tree 這個庫集合了 Redux、reselect 和異步管理的全部優勢,並且使用的代碼量更小。

在這個示例中,最明顯的一個優勢就是簡潔的語法。代碼行數僅僅比最初的 MobX 的示例多了一點點。二者都使用了簡潔的裝飾器語法。雖然須要一點時間才能真正從 MobX-State-Tree 中得到全部好處。

最重要的一點就是若是你使用過 ActiveRecord 或者其它類型的 ORM,MobX-State-Tree 就像一個具備規範化關係乾淨的數據模型。對於可擴展的應用來講,這是一個很是好的狀態管理工具。

源碼:React | React Native

Apollo GraphQL and Amazon AppSync

github.com/apollograph… aws.amazon.com/appsync/

若是你沒遇上 GraphQL 這趟列車,那你就落伍了。Apollo GraphQL + AppSync 是管理應用狀態、離線處理、請求 API、配置 GraphQL 服務器的一種很好的解決方案。許多人預測 GraphQL 會結束關於狀態管理工具的爭論。從某些角度看這很容易,但從另個角度來講也很難。

並非全部人都準備好使用 GraphQL 服務器了,但若是你準備好了,那麼 AppSync 是處理 DynamoDB 中數據一種簡單的方式。這可能須要花費更多的時間和精力才能完成並運行,但優點也是顯而易見的。

在個人示例中,我並沒真正使用那些花哨的功能。你能夠看到在等待服務器數據的延遲,我也沒有使用訂閱功能來獲取更新。這個示例能夠變得更好。但使用起來足夠簡單。哇!REST 已經成爲了歷史。

**特別說明:**請注意本例中你輸入的內容,由於它們是共享的。

源碼:React | React Native

setState + react-automata

github.com/MicheleBert…

這個庫在這幾個中比較陌生。不少時候,你都想知道 setState 是如何使用的,答案很是簡單。將狀態分解爲狀態機的作法和大多數狀態管理庫都不一樣。

經過建立一個 xstate 的狀態機配置,定義狀態如何轉換、調用和識別。所以,你必須定義出應用中用到的全部狀態,還有從一種狀態轉換到另外一種狀態的全部方式。就像在 Redux 中觸發一個 action,你須要在一個特定的事件上使用 transition 來切換到另外一個狀態。

它並非一個完整的狀態管理工具;僅僅是一個做爲你狀態管理的狀態機。

這是咱們建立狀態圖

使用狀態圖的好處使人興奮。首先,你能夠避免不想要的轉換。例如,不輸入內容就沒法轉換到 loaded 狀態。這樣能夠避免在咱們的列表中增長空白項。

其次,全部的狀態轉換均可以自動生成和測試。經過一條簡單的命令,就能夠生成多個狀態的快照。

import { testStatechart } from 'react-automata'
import { App } from '../App'
import statechart from '../Statecharts/index'

test('all state snapshots', () => {
  // 這個函數會生成對全部狀態的測試
  testStatechart({ statechart }, App)
})
複製代碼

注意:在 React Native 項目中,我不得不使用 yarn add path 來導入一些未使用的依賴項。這僅是 native 中一個比較詭異的問題。

源碼:React | React Native

Freactal

github.com/FormidableL…

固然,咱們將會展現 Formidable 實驗室這個很棒的做品。Freactal 是一個很是先進的庫,它能夠替代 [redux](https://redux.js.org/)[MobX](https://mobx.js.org/)[reselect](https://github.com/reactjs/reselect)[redux-loop](https://github.com/redux-loop/redux-loop)[redux-thunk](https://github.com/gaearon/redux-thunk)[redux-saga](https://github.com/redux-saga/redux-saga) 等這些庫了。

雖然對我來講這是上手最難的一個庫了,但我仍然認爲它有很大的價值。(譯註:這個庫中)添加更多的示例將會更有幫助。特別感謝 Ken Wheeler,他回答了我在閱讀這個庫文檔過程當中遇到的各類問題。

最終的代碼簡潔明瞭。使用到最後感受有點像 Context 的語法。我特別喜歡使用本身的命名空間來區分 effectsstatecomputer,你也能夠在其它庫中使用這個約定。

import React from 'react'
import { AddPackingItem } from 'packlist-components/native'
import { injectState } from 'freactal'

export const AddItems = ({ state, effects }) => (
  <AddPackingItem addItem={effects.addItem} setNewItemText={effects.setNewItemName} value={state.newItemName} clear={effects.clear} /> ) export default injectState(AddItems) 複製代碼

源碼:React | React Native

ReduxX

github.com/msteckyefan…

ReduxX,雖然可能在 SEO 上遇到了一些麻煩,但仍然是一個很是酷的名字。

爲何在某個名字的後面加上 X 就會很酷呢? — Gant X(jonjia X 注:做者名字是 Gant Laborde)

ReduxX 讀起來至關不錯,由於在某些方面它會讓我想起來自 Unstated 的優雅,由於咱們還在使用 React 式冗長的設置和改變狀態的代碼。看起來比較陌生的一點是,經過 getState 這個函數檢索狀態。這感受有點像鑰匙串訪問,我想知道是否能在其中加入一些證書加密的東西?引人深思啊。果真我看到了 obscureStateKeys: true,這個選項能夠將鍵名轉化爲惟一標識符。這個庫在安全方面有必定優勢。

至於如何使用它,能夠經過鍵名設置獲取狀態。就是這樣簡單!若是你不須要關心中間件,而且熟悉 keychain 全局變量,你就已經掌握了 ReduxX。

特別感謝這個庫的做者 Mikey Stecky-Efantis 能提供這個示例!

pure-store

github.com/gunn/pure-s…

100% 的測試覆蓋率,使用 MIT 證書,支持 TypeScript 的最小的狀態管理庫。這個庫感受最接近『只使用全局變量』。但實際上有一個問題,若是你肯定 update 方法接受 setState 的簽名的話,那就只有一個全局的狀態了。

特別感謝 Arthur Gunn 對這個示例的貢獻!

那些錯過的工具庫?

我知道確定還有其它的狀態管理庫在這裏並無提到,若是你知道它們,能夠在這個項目的倉庫中提交 PR。我很是樂於接受這些建議,這樣咱們均可以受益。若是有新的庫加入,我也會更新這篇文章。因此,請收好你的門票,要提交貢獻哦!博物館歡迎你的到來!😆

總結:

你不想安裝其它依賴項嗎?

使用 setState 和 Context 能夠得到至關棒的效果!爲何要引入依賴關係呢?若是你不肯定你的應用是否須要它,那就嘗試不使用外部庫。你也能夠把 Context 和像 react-automata 這樣簡單的庫結合起來使用,就會獲得一份簡潔、可測試的代碼!

你想代碼儘可能簡潔嗎?

Unstated、reduxX 和 pure-state 都很是簡潔。它們有點不一樣,優勢也不太同樣。MobX 也很容易,但你要接受裝飾器的語法。若是你能夠接受,代碼會更加可讀、優化,文檔和 stackoverflow 上的資源也頗有幫助。

你想要更好的擴展性嗎?

若是肯定你的應用須要添加不少東西,是時候拿出真正的武器了。這就是 Redux 的用武之地。若是你使用了 Redux,那麼你的技能就獲得了鍛鍊,你知道你能夠的。MobX-State-Tree 展示出告終合 MobX、選項、狀態、優化等全部的能力。這並非一次就能所有理解的內容,但每次你學到新的知識時,你就會使你的應用更增強大。

你想擁抱將來嗎?

毫無疑問,GraphQL 正在技術領域引發轟動。如今,若是使用 AppSync 進行網絡請求,或者只是使用 apollo-link-state 來管理本地數據,那麼就放棄了一些對細節的控制,但得到了回報。請密切關注上面這些庫的發展。極可能上面的許多狀態管理庫在不久的未來不得不適配 GraphQL。


Gant LabordeInfinite Red公司首席技術戰略師、做家、兼職教授、全球公開演講者和培訓中的科學家。想了解更多,能夠訪問 他的網站

致謝

感謝 Frank von HovenDerek Greenberg


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索