難道如今狀態管理不是一個能夠解決的問題嗎?直觀地說,開發人員彷佛知道一個隱藏的事實:狀態管理的使用彷佛比須要的更困難。在本文中,咱們將探討一些你可能一直在問本身的問題:javascript
想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等着你!前端
做爲前端開發人員,不只僅是佈局,開發的真正藝術之一是知道如何管理存儲狀態。簡而言之:狀態管理是複雜的,但又並不是那麼複雜。java
讓咱們看看使用React等基於組件的視圖框架/庫時的選項:react
存在於單個組件內部的狀態。在React中,經過setState
方法更新state
。git
從父級傳遞給子級的狀態。在React中,將 props
做爲屬性傳遞給子組件。github
狀態保存在根 provider (提供者) 組件中,並由 consumer (消費者) 在組件樹的某個地方訪問,而不考慮組件之間的層級關係。在 React 中,經過 context API
能夠實現。編程
大多數的狀態都是存在於視圖中的,由於它是用來反映用戶界面的。那麼,對於反映底層數據和邏輯的其它狀態,又屬於誰呢?redux
將全部內容都放在視圖中可能會致使關注點的分離:它將與javascript視圖庫聯繫在一塊兒,使代碼更難測試,並且可能最大的麻煩是:必須不斷地思考和調整存儲狀態的位置。segmentfault
狀態管理因爲設計變動而變得複雜,並且一般很難判斷哪些組件須要哪些狀態。最直接的選擇是從根組件提供全部狀態,若是真要這麼作的話,那麼選用下一種方式會更好。數組
狀態能夠移出視圖庫。而後,庫可使用提供者/消費者模式鏈接以保持同步。
也許最流行的狀態管理庫是Redux。在過去的兩年裏,它變得愈來愈受歡迎。那麼爲何這麼喜歡一個簡單的庫呢?
Redux 更具性能?答案是否認的。事實上,爲了每個必須處理的新動做(action),都會稍微慢一些。
Redux是否更簡單?固然不是。
簡單應當是純javascript:好比 TJ Holowaychuk 在twitter上說
那麼爲何不是每一個人都使用 global.state={}?
在表層之下,Redux 與 TJ 的根對象{}
徹底相同——只是包裝在了一系列實用工具的管道(pipeline)中。
在 Redux 中,不能直接修改狀態。只有一種方法:派發(Dispatch)一個動做(Action)到管道中,管道會自動根據動做去更新狀態。
沿着管道有兩組偵聽器:中間件(middleware)和訂閱(subscriptions)。 中間件是能夠偵聽傳入的動做的函數,支持諸如「logger」,「devtools」或「syncWithServer」偵聽器之類的工具。 訂閱是用於廣播這些狀態更改的函數。
最後,合成器(Reducer)函數負責把狀態變動拆分紅更小、更模塊化、更容易管理的代碼塊。
和使用一個全局對象相比,Redux 確實簡化了開發過程。
將 Redux 視爲一個帶有更新前/更新後鉤子的全局對象,以及可以以簡單的方式合成新狀態。
是的。有幾個不能否認的跡象代表 API 須要改進,這些能夠用下面的方程來總結
time_saved來表示你開發本身的解決方案所花費的時間,time_invested
至關於閱讀文檔,學習教程和研究不熟悉的概念所花費的時間。
Redux 是一個擁有陡峭學習曲線的小型庫。雖然有很多開發者可以克服深刻學習函數式編程的困難並從 Redux 獲益良多,可是也有不少開發者望而卻步,寧願從新使用 jQuery。
使用jQuery你不須要理解「monad」是什麼,你也不須要爲了使用Redux去理解函數組合。
使用 jQuery 你不須要理解「comonad」是什麼,你也不須要爲了使用 Redux 去理解函數組合。
任何框架或者庫的目的都應該是把複雜的事物抽象得更加簡單。
我認爲Redux值得重寫,至少有如下 6 個方面能夠改進得更友好。
讓咱們來看看一個基本的 Redux 初始化過程,以下圖左邊所示:
許多開發人員在第一步後就在這裏暫停,茫然地盯着深淵。 什麼是 thunk?compose?一個函數能作到這些嗎?
若是 Redux 是基於配置而不是函數組合的話,那麼像右邊那樣的初始化過程明顯看起來更加合理。
Redux 中的 reducers 能夠經過一個轉換,讓咱們遠離已經習慣但沒必要要且冗長的 switch 語句。
假設reducer
與action
類型匹配,那麼咱們能夠對參數進行反轉,這樣每一個reducer都是一個接受state
和action
的純函數。 也許更簡單,咱們能夠標準化action
並僅傳入state
和有效負載(payload)。
thunk
一般用於在 Redux 中建立異步 action。 在許多方面,thunk 的工做方式看起來更像是一個聰明的黑客,而不是官方推薦的解決方案。 咱們一步一步來看:
怎麼會這樣?一個簡單的 action 究竟是做爲一個動態類型的對象、一個函數,仍是一個 Promise?這難道不是一種拙劣的實踐嗎?
如上圖右邊所示,難道咱們就不能只使用 async/await ?
仔細想一想,其實有兩種 action
1.reducer action: 觸發 reducer 並改變狀態。
2.effect action:觸發異步 action,這可能會調用reducer操做,但異步函數不會直接更改任何狀態。
將這兩種類型的 action 區分開來,將比上面的thunk用法更有幫助,也更容易理解。
爲何咱們的標準實踐要把 action creator 和 reducer 區分開來呢?可否只用其中一個呢?改變其中一個又是否會影響到另外一個?
action creator 和 reducer 是同一枚硬幣的兩面。
const ACTION_ONE = 'ACTION_ONE'是分離 action creators 和 reducers 的一個冗餘產物。應將二者視爲一體,而且再也不須要文件導出類型的字符串。
按照使用方式,把 Redux 中所涉及的概念進行合併分組,那麼咱們能夠得出下面這個更簡單的模式。
能夠從 reducer 中自動肯定 action creator。 畢竟,在這種狀況下,reducer 能夠成爲action creator。
使用一個基本的命名約定,下面是可預測的:
reducer
命名爲 increment,那麼 type
就是 increment。更好的作法是加上命名空間 「count/increment」。
如今,從 count.increment
中,咱們能夠以一個 reducer 生成 action creator。
以上這些痛點就是咱們建立 Rematch 的緣由。
Rematch 對 Redux 進行了封裝,提供更簡單的 API,但又不失任何可配置性的特色
請參見下面的一個完整的 Rematch 示例:
在過去的幾個月裏,我一直在實際業務中使用 Rematch。做爲證實,我會說:狀態管理從未變得如此簡單、高效。
Redux 是一個出色的狀態管理工具,有鍵全的中間件生態與出色的開發工具。
Rematch 在 Redux 的基礎上構建並減小了樣板代碼和執行了一些最佳實踐。
說得清楚點,Rematch 移除了 Redux 所須要的這些東西:
讓 Redux 與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)
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)
Redux 並無被拋棄,並且也不該該被拋棄。
只是,咱們應該以更低的學習成本,更少的樣板代碼和更少的認知成本,來擁抱 Redux 背後的簡單哲學。
你的點贊是我持續分享好東西的動力,歡迎點贊!