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

jonjia發表於2018-05-17

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

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

為什麼?

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

當然,你可能對上面的某些庫很熟悉,現在你可以運用這些知識來更好地理解其它庫。你不僅有機會來領會這些庫的細節,還會發現這些庫是多麼相似。

為了通俗易懂地說明這些庫,我選擇了一個只有新增清空功能的簡單打包列表應用作為示例。

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

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

為了說明狀態如何流轉,所有的示例中,新增/清空功能是一個元件,列表功能是另一個元件。

兩個主要的元件(新增/列表)被抽象為一個需要匯入的庫,只留下基本程式碼來強調狀態的選擇。程式碼力求簡約。

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

使用每個狀態庫實現的 React 和 React Native 應用的原始碼都可以在下面的倉庫中找到。

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

github.com/GantMan/Rea…

使用上面的倉庫親自進入這些狀態庫中檢視它們吧!?


每種解決方案的個人筆記:

如果你想看程式碼,可以去檢視 GitHub 原始碼,如果你需要建議,請繼續閱讀下面這段很長的描述。

這裡我們會探討博物館中每個狀態庫的差異,正是這些差異讓每個都與眾不同。如果你有好的建議或經驗,請在評論中與大家分享。我也有興趣把這篇文章當作充滿樂趣的會議進行討論。

setState

這是狀態管理最基礎的結構,僅基於對元件及其封裝的理解。從很多方面來看,這對 React 初學者來說是一個很好的例子。顯式地將狀態提升到根元件中,所有元件都是根元件的子元件,其中定義了 props 與 state 的關係。隨著應用的增長,到子元件的顯式連線程式碼越來越複雜、脆弱,這就是不經常使用這種方法的原因。

原始碼:React | React Native

React Context

reactjs.org/docs/contex…

關於 Context 的更新引發了很多關注。實際上,在 16.x 的版本中 Context 的最終形態有點像狀態管理系統本身。簡而言之,context 設定了 providerconsumer。一個 provider 的所有子元件都可以訪問其中應用的值。所有非子元件都會看到 context 的預設值。下圖解釋了這種關係。

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

只有子元件才能繼承。

另一個非常重要的觀點,我並不喜歡 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 的狀態管理工具。

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

我不清楚它如何擴充套件或處理中介軟體等。但是如果你是狀態管理的初學者,MobX 和 Unstated 都是上手最簡單的選擇!

原始碼:React | React Native

MobX-State-Tree

github.com/mobxjs/mobx…

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

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

甚至我的同事也將它的名字縮短為 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 來切換到另一個狀態。

它並不是一個完整的狀態管理工具;僅僅是一個作為你狀態管理的狀態機。

這是我們建立狀態圖

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

使用狀態圖的好處令人興奮。首先,你可以避免不想要的轉換。例如,不輸入內容就無法轉換到 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。


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

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

致謝

感謝 Frank von HovenDerek Greenberg


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

相關文章