Rematch: 重新設計Redux

blank

Rematch: 重新設計Redux

本文是藉助網易見外的工具幫助完成翻譯的,網易見外是基於NMT(神經網絡機器翻譯)技術的海外內容智能聚合平台原文地址: hackernoon.com/redesign

到目前為止,狀態管理不應該是一個已經解決了的問題嗎?直覺上,開發者似乎知道一個隱藏的真相:狀態管理似乎比它需要的更要難。這篇文章裡,我們嘗試回答一些你可能一直在問自己的問題:

  • 你真的需要一個狀態管理庫嗎?
  • Redux的流行是否名副其實?為什麼或者為什麼不呢?
  • 我們能不能做一個更好的狀態管理方案?如果能,怎麼去做?

狀態管理真的需要一個庫嗎?

作為一個前端開發人員,不僅僅是控制像素;真正的開發之美是知道在哪裡存儲你的狀態。簡而言之:這很複雜,但又不復雜。

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

blank

1. Component State

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

2. Relative State

狀態從父組件傳遞給子組件。在React中,通過子組件上的props屬性傳遞。

3. Provider State

狀態保存在根provider (提供者)組件中,並由consumer (消費者)在組件樹的某個地方訪問,而不考慮距離。在React中,通過context API可以實現。

很多狀態都屬於視圖,因為它反映了UI。但是,其他反映你底層數據和邏輯的代碼呢?

把所有內容都放到視圖中會喪失關注點分離:它將你拴在到了某個javascript視圖庫上,這使得代碼更難測試,可能最大的麻煩是:你必須不斷思考和重新調整存儲狀態的位置。

由於設計經常改變,而且通常很難判斷哪些組件需要哪個狀態,所以狀態管理變得複雜。最簡單的選擇就是從根組件中提供所有的狀態,在這一點上,你最好選擇下一個選項。

4. External State

狀態可以移到視圖庫之外。然後,庫可以使用提供者/消費者模式“連接”,以保持同步。

也許最受歡迎的狀態管理庫是Redux。在過去的兩年裡,它變得越來越受歡迎。那麼,為什麼大家對一個簡單的庫如此熱愛呢?

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

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

簡單應當是純javascript:比如TJ Holowaychuk在twitter上說,他的狀態管理庫是{}

那麼為什麼不是每個人都使用global.state={}


為什麼是Redux?

在表層之下,Redux與TJ的根對象{}完全相同——只是包裝在了一系列實用工具的流水線(pipeline)中。

blank
redux store 的流水線

在Redux中,你不能直接修改狀態。只有一種方法:將一個動作(action)分派(dispatch)到流水線中,最終更新狀態。

在流水線中有兩組偵聽器:中間件(middleware)訂閱(subscription) 。中間件是能夠偵聽傳入的操作的函數,從而支持諸如“logger”、“devtools”或“syncWithServer”偵聽器之類的工具。訂閱是用來廣播這些狀態變化的函數。

最後,還原器(reducer)的更新方法可以將狀態變化分解為更小、更模塊化和可管理的塊。

Redux實際上可能比使用一個全局對像作為你的狀態更簡單。

可以將Redux看作一個具有前置/後置更新掛鉤的全局對象,並簡化了“還原(reducing)”下一個狀態的方法。


但是不是Redux太複雜了?

是的。有幾個明確的跡象表明API需要改進。這些可以用下面的公式來總結:

API的質量= 代碼行數/ 花在閱讀文檔上的時間

Redux是一個擁有陡峭學習曲線的小型庫。對於每一個已經克服並受益於Redux的開發人員來說,他們都是在深入研究函數式編程,另一些潛在的開發者已經失敗了,並且認為“這不是我要的,我要回到jQuery” 。

使用jQuery你不需要理解“monad”是什麼,你也不需要為了使用Redux去理解函數組合。

任何庫的目的都是通過抽象來讓更複雜的事情看起來簡單。

我的意思並不是要懟Dan Abramov(Redux的主要提交者之一)。 Redux在它早期太稚嫩的時候就已經太受歡迎了。

  • 如何重構一個已經被數百萬開發者使用的庫?
  • 你如何決定為世界各地無數的項目引入不兼容的變更?

你不能。但是你可以通過大量的文檔、教學視頻和社區活動提供驚人的支持。 Dan Abramov就是在這裡獲勝的。

或許還有另一種方法。


重新設計Redux

我認為Redux值得重寫,並且帶來了7處需要改進的地方。 (譯註:原文缺失第3點,實際只有6處)

1.設置

讓我們看一看左邊的真實世界Redux範例的基本設置。

blank

許多開發人員在第一步後就在這裡暫停,茫然地盯著深淵。
什麼是thunk? compose?一個函數能做到這些嗎?

倘若Redux可以基於配置而非組合。設置可能看起來更像右邊的範例。

2.簡化reducers

Redux中的reducers可以通過一個轉換,讓我們遠離已經習慣但不必要且冗長的switch語句。

blank

既然一個reducer是為了匹配動作的類型(action.type),那麼我們可以對參數進行反轉,以便讓每個reducer都是一個接受狀態和動作的純函數。也許更簡單,我們可以標準化動作,只傳遞狀態和有效負載(payload)。

4. Async/Await與thunks

在Redux中,通常使用thunks來創建異步操作。在很多方面,一個thunk 的工作方式看起來更像是一個聰明的黑客,而不是官方推薦的解決方案。跟我來往下看:

  1. 你分派一個動作(dispatch an action),它實際上是一個函數而不是預期的對象。
  2. 這個中間件檢查每一個動作,看它是否是一個函數。
  3. 如果是,中間件調用該函數,並傳入一些store 的方法:dispatch 和getState。

怎麼會這樣?一個簡單的action 到底是作為一個動態類型的對象、一個函數,還是一個Promise?這難道不是一種拙劣的實踐嗎?

blank

就像右邊的例子一樣,我們難道不能只是簡單地使用async/await 就可以了嗎?

5.兩種actions

當你思考這個問題的時候,實際上有兩種actions:

  1. reducer action :觸發一個reducer並改變狀態。
  2. effect action :觸發一個異步操作。這可能會調用reducer action,但是異步函數不會直接改變任何狀態。

區分這兩種類型的actions 會更有幫助,並且不會讓上述用法與“thunks”混淆。

6.不再有動作類型(action.type)變量

為什麼用不同的方式來對待action creators 和reducers 是一種標準做法?兩者之一可以獨立於另一方而存在嗎?改變其中之一不會影響另一方嗎?

action creators 和reducers 是同一枚硬幣的兩面

const ACTION_ONE = 'ACTION_ONE'是分離action creators和reducers的一個多餘的副產品。應將兩者視為一體,並且不再需要文件導出類型的字符串。

7. reducers就是action creators

通過使用Redux的元素來分組,你可能會得到一個更簡單的模式。

blank

它可以自動地從reducer中確定action creator。畢竟,在這個場景中, reducer可以成為action creator

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

  1. 如果一個reducer有“increment”的名字,那麼類型就是“increment”。更妙的是,我們還給它增加了一個命名空間“count/increment”。
  2. 每個action都通過一個“payload”鍵傳遞數據。
blank

現在,從count.increment中,我們可以以一個reducer生成action creator。


好消息:我們可以有一個更好的Redux

這些痛點是我們開發Rematch的原因。

blank

Rematch圍繞Redux進行了封裝,提供更簡單的API,而不丟失任何可配置性。

blank

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

blank

在過去的幾個月裡,我一直在實際業務中使用Rematch。作為證明,我會說:

我從未花費如此少的時間去考慮狀態管理。

Redux不會消失,也不應該消失。擁抱Redux之後的簡單模式,學習曲線更平滑,樣板代碼(boilerplate)更少,認知開銷也更少。

試試Rematch ,看看你是否喜歡它。並且在github上給我們一個小星星⭐️以便讓別人也知道。

What do you think?

Written by marketer

blank

基於Immutable.js 實現撤銷重做功能

blank

[多行文本] 樣式怎麼沒了?