跳至主要內容

Redux 常見問題:設計決策

目錄

設計決策

為什麼 Redux 不會將狀態和動作傳遞給訂閱者?

訂閱者旨在回應狀態值本身,而不是動作。狀態更新會同步處理,但訂閱者通知可以批次處理或延遲處理,這表示訂閱者並非總是會在每次動作時收到通知。這是一種常見的 效能最佳化,用於避免重複重新渲染。

批次處理或防抖動可以使用增強器覆寫 store.dispatch 來改變訂閱者被通知的方式。此外,還有程式庫會改變 Redux 來批次處理動作以最佳化效能並避免重複重新渲染

  • redux-batch 允許將動作陣列傳遞給 store.dispatch(),只會有一個通知,
  • redux-batched-subscribe 允許批次處理訂閱通知,這些通知是因調度而產生的。

預期的保證是 Redux 最終會呼叫所有訂閱者,並提供最新的可用狀態,但並非總是會為每個動作呼叫每個訂閱者。只要呼叫 store.getState(),訂閱者就可以取得儲存狀態。無法在訂閱者中提供動作,否則會破壞動作的批次處理方式。

在訂閱者中使用動作的潛在用例(這是一個不受支援的功能)是確保組件僅在某些動作後重新渲染。應該透過以下方式控制重新渲染

  1. shouldComponentUpdate 生命週期方法
  2. 虛擬 DOM 相等性檢查 (vDOMEq)
  3. React.PureComponent
  4. 使用 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 的中間件得以執行。

進一步資訊

討論

  • 為什麼 applyMiddleware 會將封閉用於 dispatch?

為什麼 combineReducers 在呼叫每個 reducer 時不包含包含整個狀態的第三個參數?

combineReducers 堅持鼓勵按網域分割 reducer 邏輯。正如 超越 combineReducers 中所述,combineReducers 故意限制為處理單一常見用例:透過委派更新每個狀態區段的工作給特定區段 reducer,來更新狀態樹(一個純粹的 JavaScript 物件)。

每個 reducer 的潛在第三個參數應該是什麼並不顯而易見:整個狀態樹、某些回呼函數、狀態樹的其他部分等。如果 combineReducers 不符合你的用例,請考慮使用像 combineSectionReducersreduceReducers 這樣的函式庫,以取得具有深度巢狀 reducer 和需要存取全域狀態的 reducer 的其他選項。

如果沒有已發布的公用程式解決你的用例,你可以隨時自己撰寫一個函數,來執行你需要的功能。

進一步的資訊

文章

討論

為什麼 mapDispatchToProps 不允許使用來自 getState()mapStateToProps() 的回傳值?

有要求在 mapDispatch 內部使用整個 statemapState 的回傳值,以便在 mapDispatch 內部宣告函數時,它們可以封閉來自儲存體的最新回傳值。

此方法在 mapDispatch 中不受支援,因為這表示每次更新儲存體時,也必須呼叫 mapDispatch。這會導致每次更新狀態時重新建立函數,從而增加許多效能負擔。

處理此用例(需要根據目前狀態和 mapDispatchToProps 函數變更 props)的建議方法,是使用 connect 函數的第三個參數 mergeProps。如果指定,它會傳遞 mapStateToProps()mapDispatchToProps() 和容器元件的 props 的結果。從 mergeProps 回傳的純粹物件會作為 props 傳遞給包裝元件。

進一步的資訊

討論