Redux 常見問題:設計決策
目錄
- 為什麼 Redux 不會將狀態和動作傳遞給訂閱者?
- 為什麼 Redux 不支援使用類別作為動作和 Reducer?
- 為什麼中間件簽章使用柯里化?
- 為什麼 applyMiddleware 會將封閉用於 dispatch?
- 為什麼
combineReducers
在呼叫每個 Reducer 時不包含第三個參數,也就是整個狀態? - 為什麼 mapDispatchToProps 不允許使用
getState()
或mapStateToProps()
的回傳值?
設計決策
為什麼 Redux 不會將狀態和動作傳遞給訂閱者?
訂閱者旨在回應狀態值本身,而不是動作。狀態更新會同步處理,但訂閱者通知可以批次處理或延遲處理,這表示訂閱者並非總是會在每次動作時收到通知。這是一種常見的 效能最佳化,用於避免重複重新渲染。
批次處理或防抖動可以使用增強器覆寫 store.dispatch
來改變訂閱者被通知的方式。此外,還有程式庫會改變 Redux 來批次處理動作以最佳化效能並避免重複重新渲染
- redux-batch 允許將動作陣列傳遞給
store.dispatch()
,只會有一個通知, - redux-batched-subscribe 允許批次處理訂閱通知,這些通知是因調度而產生的。
預期的保證是 Redux 最終會呼叫所有訂閱者,並提供最新的可用狀態,但並非總是會為每個動作呼叫每個訂閱者。只要呼叫 store.getState()
,訂閱者就可以取得儲存狀態。無法在訂閱者中提供動作,否則會破壞動作的批次處理方式。
在訂閱者中使用動作的潛在用例(這是一個不受支援的功能)是確保組件僅在某些動作後重新渲染。應該透過以下方式控制重新渲染
- shouldComponentUpdate 生命週期方法
- 虛擬 DOM 相等性檢查 (vDOMEq)
- React.PureComponent
- 使用 React-Redux:使用 mapStateToProps 讓組件僅訂閱他們需要的儲存部分。
更多資訊
文章
討論
為什麼 Redux 不支援使用類別來處理動作和簡化器?
使用稱為動作建立器的函式來傳回動作物件的模式,對於擁有大量物件導向程式設計經驗的程式設計師來說,可能看起來很反直覺,他們會認為這是類別和實例的強大用例。不支援動作物件和簡化器的類別實例,因為類別實例會讓序列化和反序列化變得棘手。像 JSON.parse(string)
這類的反序列化方法會傳回一般的 Javascript 物件,而不是類別實例。
如 儲存常見問題集 中所述,如果您不介意持久性和時間旅行除錯無法按預期運作,歡迎將不可序列化的項目放入您的 Redux 儲存中。
序列化讓瀏覽器能夠以更少的記憶體儲存所有已調度的動作,以及先前的儲存狀態。倒帶和「熱重載」儲存是 Redux 開發人員體驗和 Redux DevTools 功能的核心。這也讓反序列化的動作能夠儲存在伺服器中,並在使用 Redux 進行伺服器端渲染時在瀏覽器中重新序列化。
進一步資訊
文章
討論
為什麼中間件簽章使用柯里化?
Redux 中間件使用三重巢狀函式結構撰寫,看起來像 const middleware = storeAPI => next => action => {}
,而不是看起來像 const middleware = (storeAPI, next, action) => {}
的單一函式。有幾個原因造成這種情況。
其中一個原因是「柯里化」函式是一種標準函式程式設計技巧,而 Redux 明確打算在其設計中使用函式程式設計原則。另一個原因是柯里化函式會建立封閉,您可以在其中宣告在中間件生命週期中存在的變數(這可以視為在類別實例生命週期中存在的實例變數的函式等效項)。最後,這只是在 Redux 最初設計時所選擇的方法。
宣告中間件的 柯里化函式簽章 被一些人 視為不必要,因為在執行 applyMiddleware 函式時 store 和 next 都可用。此問題已判定不 值得引入重大變更,因為 Redux 生態系統中現在有數百個依賴於現有中間件定義的中間件。
進一步資訊
討論
為什麼 applyMiddleware
會對 dispatch
使用封閉?
applyMiddleware
會從儲存中取得現有的 dispatch,並對其進行封閉,以建立最初的中間件鏈,而這些中間件已使用公開 getState 和 dispatch 函式的物件進行呼叫,這讓依賴於初始化期間 dispatch 的中間件得以執行。
進一步資訊
討論
為什麼 combineReducers
在呼叫每個 reducer 時不包含包含整個狀態的第三個參數?
combineReducers
堅持鼓勵按網域分割 reducer 邏輯。正如 超越 combineReducers
中所述,combineReducers
故意限制為處理單一常見用例:透過委派更新每個狀態區段的工作給特定區段 reducer,來更新狀態樹(一個純粹的 JavaScript 物件)。
每個 reducer 的潛在第三個參數應該是什麼並不顯而易見:整個狀態樹、某些回呼函數、狀態樹的其他部分等。如果 combineReducers
不符合你的用例,請考慮使用像 combineSectionReducers 或 reduceReducers 這樣的函式庫,以取得具有深度巢狀 reducer 和需要存取全域狀態的 reducer 的其他選項。
如果沒有已發布的公用程式解決你的用例,你可以隨時自己撰寫一個函數,來執行你需要的功能。
進一步的資訊
文章
討論
為什麼 mapDispatchToProps
不允許使用來自 getState()
或 mapStateToProps()
的回傳值?
有要求在 mapDispatch
內部使用整個 state
或 mapState
的回傳值,以便在 mapDispatch
內部宣告函數時,它們可以封閉來自儲存體的最新回傳值。
此方法在 mapDispatch
中不受支援,因為這表示每次更新儲存體時,也必須呼叫 mapDispatch
。這會導致每次更新狀態時重新建立函數,從而增加許多效能負擔。
處理此用例(需要根據目前狀態和 mapDispatchToProps 函數變更 props)的建議方法,是使用 connect 函數的第三個參數 mergeProps。如果指定,它會傳遞 mapStateToProps()
、mapDispatchToProps()
和容器元件的 props 的結果。從 mergeProps
回傳的純粹物件會作為 props 傳遞給包裝元件。
進一步的資訊
討論