【譯】Rematch: Redux 的從新設計

譯者:前端小智javascript

原文:hackernoon.com/redesigning…前端

難道如今狀態管理不是一個能夠解決的問題嗎?直觀地說,開發人員彷佛知道一個隱藏的事實:狀態管理的使用彷佛比須要的更困難。在本文中,咱們將探討一些你可能一直在問本身的問題:java

  • 你是否須要一個用於狀態管理的庫?
  • Redux 的受歡迎程度是否值得咱們去使用? 爲何或者爲何不值得?
  • 咱們可否制定更好狀態管理解決方案嗎?若是能,要怎麼作?

想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等着你!react

狀態管理須要一個庫嗎

做爲前端開發人員,不只僅是佈局,開發的真正藝術之一是知道如何管理存儲狀態。簡而言之:狀態管理是複雜的,但又並不是那麼複雜。git

讓咱們看看使用React等基於組件的視圖框架/庫時的選項:github

1. Component State (組件狀態)

存在於單個組件內部的狀態。在React中,經過setState方法更新state編程

2. Relative State (關聯狀態)

從父級傳遞給子級的狀態。在React中,將 props 做爲屬性傳遞給子組件。redux

3. Provided State (供給狀態)

狀態保存在根 provider (提供者) 組件中,並由 consumer (消費者) 在組件樹的某個地方訪問,而不考慮組件之間的層級關係。在 React 中,經過 context API 能夠實現。數組

大多數的狀態都是存在於視圖中的,由於它是用來反映用戶界面的。那麼,對於反映底層數據和邏輯的其它狀態,又屬於誰呢?框架

將全部內容都放在視圖中可能會致使關注點的分離:它將與javascript視圖庫聯繫在一塊兒,使代碼更難測試,並且可能最大的麻煩是:必須不斷地思考和調整存儲狀態的位置。

狀態管理因爲設計變動而變得複雜,並且一般很難判斷哪些組件須要哪些狀態。最直接的選擇是從根組件提供全部狀態,若是真要這麼作的話,那麼選用下一種方式會更好。

4. External State (外部狀態)

狀態能夠移出視圖庫。而後,庫可使用提供者/消費者模式鏈接以保持同步。

也許最流行的狀態管理庫是Redux。在過去的兩年裏,它變得愈來愈受歡迎。那麼爲何這麼喜歡一個簡單的庫呢?

Redux 更具性能?答案是否認的。事實上,爲了每個必須處理的新動做(action),都會稍微慢一些。

Redux是否更簡單?固然不是。

簡單應當是純javascript:好比 TJ Holowaychuk 在twitter上說

那麼爲何不是每一個人都使用 global.state={}?

爲何使用 Redux

在表層之下,Redux 與 TJ 的根對象{}徹底相同——只是包裝在了一系列實用工具的管道(pipeline)中。

在 Redux 中,不能直接修改狀態。只有一種方法:**派發(Dispatch)一個動做(Action)**到管道中,管道會自動根據動做去更新狀態。

沿着管道有兩組偵聽器:中間件(middleware)訂閱(subscriptions)。 中間件是能夠偵聽傳入的動做的函數,支持諸如「logger」,「devtools」或「syncWithServer」偵聽器之類的工具。 訂閱是用於廣播這些狀態更改的函數。

最後,**合成器(Reducer)**函數負責把狀態變動拆分紅更小、更模塊化、更容易管理的代碼塊。

和使用一個全局對象相比,Redux 確實簡化了開發過程。

將 Redux 視爲一個帶有更新前/更新後鉤子的全局對象,以及可以以簡單的方式合成新狀態。

Redux 是否是太複雜了?

是的。有幾個不能否認的跡象代表 API 須要改進,這些能夠用下面的方程來總結

time_saved來表示你開發本身的解決方案所花費的時間,time_invested至關於閱讀文檔,學習教程和研究不熟悉的概念所花費的時間。

Redux 是一個擁有陡峭學習曲線的小型庫。雖然有很多開發者可以克服深刻學習函數式編程的困難並從 Redux 獲益良多,可是也有不少開發者望而卻步,寧願從新使用 jQuery。

使用jQuery你不須要理解「monad」是什麼,你也不須要爲了使用Redux去理解函數組合。

使用 jQuery 你不須要理解「comonad」是什麼,你也不須要爲了使用 Redux 去理解函數組合。

任何框架或者庫的目的都應該是把複雜的事物抽象得更加簡單。

從新設計Redux

我認爲Redux值得重寫,至少有如下 6 個方面能夠改進得更友好。

1.初始化

讓咱們來看看一個基本的 Redux 初始化過程,以下圖左邊所示:

許多開發人員在第一步後就在這裏暫停,茫然地盯着深淵。 什麼是 thunkcompose?一個函數能作到這些嗎?

若是 Redux 是基於配置而不是函數組合的話,那麼像右邊那樣的初始化過程明顯看起來更加合理。

2. 簡化 reducers

Redux 中的 reducers 能夠經過一個轉換,讓咱們遠離已經習慣但沒必要要且冗長的 switch 語句。

假設reduceraction類型匹配,那麼咱們能夠對參數進行反轉,這樣每一個reducer都是一個接受stateaction的純函數。 也許更簡單,咱們能夠標準化action並僅傳入state和有效負載(payload)。

3.使用 Async/Await 代替 Thunks

thunk 一般用於在 Redux 中建立異步 action。 在許多方面,thunk 的工做方式看起來更像是一個聰明的黑客,而不是官方推薦的解決方案。 咱們一步一步來看:

  1. 你派發一個action(dispatch an action),它其實是一個函數而不是預期的對象。

  2. thunk 中間件檢查每一個動做,看看它是不是一個函數。

  3. 若是是,中間件調用該函數,並傳入一些 store 的方法:dispatchgetState

怎麼會這樣?一個簡單的 action 究竟是做爲一個動態類型的對象、一個函數,仍是一個 Promise?這難道不是一種拙劣的實踐嗎?

如上圖右邊所示,難道咱們就不能只使用 async/await ?

4. 兩種 action

仔細想一想,其實有兩種 action

1.reducer action: 觸發 reducer 並改變狀態。

2.effect action:觸發異步 action,這可能會調用reducer操做,但異步函數不會直接更改任何狀態。

將這兩種類型的 action 區分開來,將比上面的thunk用法更有幫助,也更容易理解。

5. 再也不有 action 類型(action.type)變量

爲何咱們的標準實踐要把 action creator 和 reducer 區分開來呢?可否只用其中一個呢?改變其中一個又是否會影響到另外一個?

action creator 和 reducer 是同一枚硬幣的兩面。

**const ACTION_ONE = 'ACTION_ONE'**是分離 action creators 和 reducers 的一個冗餘產物。應將二者視爲一體,而且再也不須要文件導出類型的字符串。

6.reducers 即 action creators

按照使用方式,把 Redux 中所涉及的概念進行合併分組,那麼咱們能夠得出下面這個更簡單的模式。

能夠從 reducer 中自動肯定 action creator。 畢竟,在這種狀況下,reducer 能夠成爲action creator

使用一個基本的命名約定,下面是可預測的:

  1. 若是 reducer 命名爲 increment,那麼 type 就是 increment。更好的作法是加上命名空間 「count/increment」

  2. 每一個 action 都經過 payload 鍵來傳遞數據。

如今,從 count.increment 中,咱們能夠以一個 reducer 生成 action creator。

好消息:咱們能夠有一個更好的 Redux

以上這些痛點就是咱們建立 Rematch 的緣由。

Rematch 對 Redux 進行了封裝,提供更簡單的 API,但又不失任何可配置性的特色

請參見下面的一個完整的 Rematch 示例:

在過去的幾個月裏,我一直在實際業務中使用 Rematch。做爲證實,我會說:狀態管理從未變得如此簡單、高效。

Redux 與 Rematch 的對比

Redux 是一個出色的狀態管理工具,有鍵全的中間件生態與出色的開發工具。

Rematch 在 Redux 的基礎上構建並減小了樣板代碼和執行了一些最佳實踐。

說得清楚點,Rematch 移除了 Redux 所須要的這些東西:

  • 聲明 action 類型
  • action 建立函數
  • thunks
  • store 配置
  • mapDispatchToProps
  • sagas

讓 Redux 與Rematch 做對比有助於讓理解更加清晰。

Rematch

1.model

import { init } from '@rematch/core'

const count = {
  state: 0,
  reducers: {
    upBy: (state, payload) => state + payload
  }
}

init({
  model: { count }
})
複製代碼

2.View

import { connect } from 'react-redux'

// Component

const mapStateToProps = (state) => ({
  count: state.count
})

const mapDispatchToProps = (dispatch) => ({
  countUpBy: dispatch.count.upBy
})

connect(mapStateToProps, mapDispatchToProps)(Component)
複製代碼

Redux (最佳實踐)

1.store

import { createStore, combineReducers } from 'redux'
// devtools, reducers, middleware, etc.
export default createStore(reducers, initialState, enhancers)
複製代碼

2.Action Type

export const COUNT_UP_BY = 'COUNT_UP_BY'
複製代碼

3.Action Creator

import { COUNT_UP_BY } from '../types/counter'

export const countUpBy = (value) => ({
  type: COUNT_UP_BY,
  payload: value,
})
複製代碼

4.Reducer

import { COUNT_UP_BY } from '../types/counter'

const initialState = 0

export default (state = initialState, action) => {
  switch (action.type) {
    case COUNT_UP_BY:
      return state + action.payload
    default: return state
  }
}
複製代碼

5.view

import { countUpBy } from '../actions/count'
import { connect } from 'react-redux'

// Component

const mapStateToProps = (state) => ({
  count: state.count,
})

connect(mapStateToProps, { countUpBy })(Component)
複製代碼

Rudex 與 Rematch 的分數板

Redux 並無被拋棄,並且也不該該被拋棄。

只是,咱們應該以更低的學習成本,更少的樣板代碼和更少的認知成本,來擁抱 Redux 背後的簡單哲學。

你的點贊是我持續分享好東西的動力,歡迎點贊!

交流

乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。

github.com/qq449245884…

我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!

關注公衆號,後臺回覆福利,便可看到福利,你懂的。

相關文章
相關標籤/搜索