Redux 常見問題:動作
目錄
動作
為什麼 type
應該是一個字串?為什麼我的動作類型應該是常數?
與狀態一樣,可序列化動作啟用了 Redux 的多項定義功能,例如時間旅行除錯,以及記錄和重播動作。如果使用類似於 Symbol
的 type
值或使用 instanceof
檢查動作本身,將會中斷此功能。字串是可序列化的且易於自述,因此是更好的選擇。請注意,如果動作供中間件使用,則在動作中使用符號、承諾或其他不可序列化值是可以的。動作僅需要在實際到達儲存區並傳遞給簡化器時才需要可序列化。
由於效能原因,我們無法可靠地強制執行可序列化動作,因此 Redux 僅檢查每個動作是否為純粹物件,以及 type
是否為字串。其餘部分取決於您,但您可能會發現保持所有內容可序列化有助於除錯和重現問題。
封裝和集中常用的程式碼片段是程式設計中的關鍵概念。雖然可以在任何地方手動建立動作物件,並手動撰寫每個 type
值,但定義可重複使用的常數可以讓維護程式碼變得更輕鬆。如果您將常數放在一個獨立的檔案中,您可以檢查您的 import
陳述是否有錯字,這樣您就不會意外使用錯誤的字串。
進一步資訊
文件
討論
- #384:建議將動作常數命名為過去式
- #628:使用較少樣板建立簡單動作的解決方案
- #1024:建議:宣告式簡化器
- #1167:沒有 switch 的簡化器
- Stack Overflow:為什麼您需要「動作」作為 Redux 中的資料?
- Stack Overflow:Redux 中的常數有什麼作用?
簡化器和動作之間是否總是一對一的對應?
否。我們建議您撰寫獨立的小簡化器函式,每個函式負責更新狀態的特定區塊。我們稱此模式為「簡化器組合」。特定動作可以由全部、部分或沒有任何簡化器處理。這讓元件與實際資料變更保持分離,因為一個動作可能會影響狀態樹的不同部分,而且元件不需要知道這一點。有些使用者選擇將它們更緊密地結合在一起,例如「ducks」檔案結構,但預設情況下絕對沒有進行一對一的對應,而且當您覺得想要在多個簡化器中處理一個動作時,您應該跳脫這種範例。
進一步資訊
文件
討論
- Twitter:最常見的 Redux 誤解
- #1167:沒有 switch 的簡化器
- Reduxible #8:簡化器和動作建立器並非一對一的對應
- Stack Overflow:我可以在沒有 Redux Thunk 中介軟體的情況下發送多個動作嗎?
我如何表示「副作用」,例如 AJAX 呼叫?為什麼我們需要「動作建立器」、「thunk」和「中介軟體」等東西來執行非同步行為?
這是一個漫長且複雜的主題,對於程式碼應如何組織以及應使用哪些方法,有各種不同的意見。
任何有意義的網路應用程式都需要執行複雜的邏輯,通常包括非同步工作,例如發出 AJAX 要求。該程式碼不再純粹是其輸入的函數,與外部世界的互動稱為「副作用」
Redux 受到函數式程式設計的啟發,開箱即用,沒有執行副作用的地方。特別是,簡化器函數必須始終是 (state, action) => newState
的純函數。但是,Redux 的中介軟體可以攔截發送的動作並在它們周圍新增額外的複雜行為,包括副作用。
一般來說,Redux 建議具有副作用的程式碼應該是動作建立過程的一部分。雖然該邏輯可以在 UI 元件內執行,但通常有意義將該邏輯抽取到可重複使用的函數中,以便可以從多個地方呼叫相同的邏輯,換句話說,一個動作建立器函數。
執行此操作最簡單且最常見的方法是新增Redux Thunk中介軟體,它允許您撰寫具有更複雜且非同步邏輯的動作建立器。另一種廣泛使用的方法是Redux Saga,它允許您使用產生器撰寫更多同步的程式碼,並且可以在 Redux 應用程式中充當「背景執行緒」或「守護程式」。另一種方法是Redux Loop,它透過允許您的簡化器宣告對狀態變更的副作用並讓它們分開執行,來反轉此過程。除此之外,還有許多其他社群開發的函式庫和想法,每個函式庫和想法都有自己對如何管理副作用的看法。
進一步資訊
文件
文章
討論
- #291:嘗試將 API 呼叫放在正確的地方
- #455:建模副作用
- #533:非同步動作建立函式的更簡單介紹
- #569:建議:明確副作用的 API
- #1139:基於產生器和 sagas 的替代副作用模型
- Stack Overflow:為什麼我們需要 Redux 中的非同步流程中介軟體?
- Stack Overflow:如何發送具有逾時設定的 Redux 動作?
- Stack Overflow:我應該將與 redux 中動作連結的同步副作用放在哪裡?
- Stack Overflow:如何在 Redux 中處理複雜的副作用?
- Stack Overflow:如何單元測試非同步 Redux 動作以模擬 ajax 回應
- Stack Overflow:如何針對 Redux 中的狀態變更發出 AJAX 呼叫?
- Reddit:協助使用 Redux-Promise 中介軟體執行非同步 API 呼叫。
- Twitter:sagas、迴圈和其他方法之間可能的比較
我應該使用哪個非同步中介軟體?你如何決定 thunk、sagas、可觀察物件或其他方法?
有許多可用的非同步/副作用中介軟體,但最常使用的為 redux-thunk
、redux-saga
和 redux-observable
。這些是不同的工具,具有不同的優點、缺點和使用案例。
一般來說
- Thunk 最適合複雜的同步邏輯(特別是需要存取整個 Redux 儲存狀態的程式碼)和簡單的非同步邏輯(例如基本的 AJAX 呼叫)。透過使用
async/await
,也可以合理地使用 thunk 處理一些更複雜的基於承諾的邏輯。 - Sagas 最適合複雜的非同步邏輯和解耦的「背景執行緒」類型行為,特別是如果你需要監聽已發送的動作(這是 thunk 無法執行的)。它們需要熟悉產生器函式和
redux-saga
的「效果」運算子。 - 可觀察物件解決與 sagas 相同的問題,但仰賴 RxJS 來實作非同步行為。它們需要熟悉 RxJS API。
我們建議大多數 Redux 使用者從 thunk 開始,然後在他們的應用程式確實需要處理更複雜的非同步邏輯時,再新增一個額外的副作用函式庫,例如 sagas 或可觀察物件。
由於 sagas 和可觀察物件具有相同的用例,因此應用程式通常會使用其中一種,但不會同時使用兩種。但是,請注意,同時使用 thunk 和 sagas 或可觀察物件絕對沒問題,因為它們解決了不同的問題。
文章
討論
- Reddit:討論同時使用 thunk 和 sagas,以及 sagas 的優缺點
- Stack Overflow:使用 Redux-Saga 搭配 ES2015 產生器與使用 Redux-Thunk 搭配 ES2017 非同步/等待的優缺點
- Stack Overflow:為什麼要使用 Redux-Observable 而不用 Redux-Saga?
我應該從一個動作建立器中連續發送多個動作嗎?
對於如何建構動作,沒有具體的規則。使用非同步中間件(例如 Redux Thunk)肯定能實現以下場景:連續發送多個不同但相關的動作、發送動作來表示 AJAX 要求的進度、根據狀態有條件地發送動作,甚至發送動作並立即檢查更新後的狀態。
一般來說,請詢問這些動作是否相關但獨立,或是否應實際表示為一個動作。執行對您自己的情況有意義的動作,但請嘗試平衡簡化器和動作記錄的可讀性。例如,包含整個新狀態樹的動作會讓您的簡化器變成一行,但缺點是您現在沒有變更發生的原因記錄,因此除錯變得非常困難。另一方面,如果您在迴圈中發出動作以保持它們的細緻程度,這表示您可能想要引入以不同方式處理的新動作類型。
在您擔心效能的地方,請盡量避免連續多次同步發送。有許多附加元件和方法也可以批次處理發送。
進一步資訊
文件
文章
討論