目錄
1. 從 userDetails(第一個 API 響應)中獲取 city
2. 基於用戶 city,我們將獲取該 city 中的所有餐廳
Promises
此方法的缺點
我們如何改進?
使用 Redux Thunk
使用 Redux-Saga
Saga 如何工作?
使用 Redux-Observables
我的偏好
關於 React-Redux 應用程序中異步操作的常見問題
中間件在處理 Redux 中的異步操作中的作用是什麼?
Redux Thunk 如何幫助管理異步操作?
Redux Thunk 和 Redux Saga 之間的區別是什麼?
如何在 Redux 中處理異步操作中的錯誤?
如何在 Redux 中測試異步 action?
如何在 Redux 中取消異步操作?
如何在 Redux 中處理異步操作中的競爭條件?
如何將 async/await 與 Redux Thunk 一起使用?
如何在 Redux 中的異步操作中處理加載狀態?
如何在 Redux 中處理副作用?
首頁 web前端 js教程 React Redux應用中的異步操作

React Redux應用中的異步操作

Feb 16, 2025 pm 12:02 PM

Async Operations in React Redux Applications

關鍵要點

  • JavaScript 的單線程特性意味著異步操作(如 API 調用)對於非阻塞式 UI 至關重要,高效處理這些操作在 React Redux 應用中至關重要。
  • Redux Thunk、Redux-Saga 和 Redux-Observables 是流行的 Redux 異步操作管理中間件,它們各自根據應用的複雜性和需求提供不同的優勢。
  • Redux Thunk 通過允許返回函數而不是 action 來簡化異步 action 分發,從而能夠在這些函數中進行順序 API 調用和處理相關數據。
  • Redux-Saga 利用 ES6 生成器為複雜場景(如處理競爭條件、暫停和取消操作)提供更強大的解決方案,使其適合大型應用。
  • Redux-Observables 利用 RxJS 提供了一種強大的方法來通過 action 流管理副作用,在復雜的應用程序架構中提供出色的可擴展性和對異步操作的控制。

此文章最初發表於 Codebrahma。

JavaScript 是一種單線程編程語言。也就是說,當你編寫如下代碼時……

Async Operations in React Redux Applications

……第二行只有在第一行執行完畢後才會執行。大多數情況下這不會有問題,因為客戶端或服務器每秒執行數百萬次計算。只有當我們執行代價高昂的計算(需要相當長的時間才能完成的任務——網絡請求需要一些時間才能返回)時,我們才會注意到這些影響。

為什麼我只在這裡展示 API 調用(網絡請求)?其他異步操作呢? API 調用是一個非常簡單且有用的例子,用於描述如何處理異步操作。還有其他操作,例如 setTimeout()、性能密集型計算、圖像加載和任何事件驅動的操作。

在構建應用程序時,我們需要考慮異步執行如何影響結構。例如,將 fetch() 視為從瀏覽器執行 API 調用(網絡請求)的函數。 (忽略它是否是 AJAX 請求。只需將其行為視為異步或同步。)請求在服務器上處理時經過的時間不會發生在主線程上。因此,您的 JS 代碼將繼續執行,一旦請求返迴響應,它將更新線程。

考慮這段代碼:

userId = fetch(userEndPoint); // 从 userEndpoint 获取 userId
userDetails = fetch(userEndpoint, userId) // 为此特定 userId 获取数据。
登入後複製
登入後複製
登入後複製
登入後複製

在這種情況下,由於 fetch() 是異步的,當我們嘗試獲取 userDetails 時,我們將不會擁有 userId。因此,我們需要以一種確保第二行僅在第一行返迴響應後才執行的方式來構建它。

大多數現代網絡請求實現都是異步的。但這並不總是有效,因為我們依賴於先前 API 響應數據來進行後續 API 調用。讓我們看看如何在 ReactJS/Redux 應用程序中特別構建它。

React 是一個用於創建用戶界面的前端庫。 Redux 是一個狀態容器,可以管理應用程序的整個狀態。將 React 與 Redux 結合使用,我們可以創建高效且可擴展的應用程序。在這樣的 React 應用程序中,有多種方法可以構建異步操作。對於每種方法,我們將討論其在以下因素方面的優缺點:

  • 代碼清晰度
  • 可擴展性
  • 易於錯誤處理

對於每種方法,我們將執行這兩個 API 調用:

1. 從 userDetails(第一個 API 響應)中獲取 city

假設端點是 /details。它將在響應中包含城市。響應將是一個對象:

userId = fetch(userEndPoint); // 从 userEndpoint 获取 userId
userDetails = fetch(userEndpoint, userId) // 为此特定 userId 获取数据。
登入後複製
登入後複製
登入後複製
登入後複製

2. 基於用戶 city,我們將獲取該 city 中的所有餐廳

假設端點是 /restuarants/:city。響應將是一個數組:

userDetails: {
  …
  city: 'city',
  …
};
登入後複製
登入後複製

請記住,我們只有在完成第一個請求後才能發出第二個請求(因為它依賴於第一個請求)。讓我們看看各種方法:

  • 直接使用 Promise 或 async/await 與 setState
  • 使用 Redux Thunk
  • 使用 Redux-Saga
  • 使用 Redux Observables

我特別選擇了上述方法,因為它們是大型項目中最常用的方法。仍然存在其他方法可能更特定於特定任務,並且不具備複雜應用程序所需的所有功能(例如 redux-async、redux-promise、redux-async-queue)。

Promises

Promise 是一個對象,它可能在將來的某個時間產生單個值:已解析的值或未解析的原因(例如,發生網絡錯誤)。 — Eric Elliot

在我們的例子中,我們將使用 axios 庫來獲取數據,當我們發出網絡請求時,它會返回一個 Promise。該 Promise 可能會解析並返迴響應或拋出錯誤。因此,一旦 React 組件 掛載,我們可以直接像這樣獲取:

['restaurant1', 'restaurant2', …]
登入後複製

通過這種方式,當狀態發生變化(由於獲取)時,組件 將自動重新渲染並加載餐廳列表。

Async/await 是一種新的實現,我們可以用它來進行異步操作。例如,可以通過以下方式實現相同的功能:

componentDidMount() {
  axios.get('/details') // 获取用户详细信息
    .then(response => {
      const userCity = response.city;
      axios.get(`/restaurants/${userCity}`)
        .then(restaurantResponse => {
          this.setState({
            listOfRestaurants: restaurantResponse, // 设置状态
          })
        })
    })
}
登入後複製

這兩種方法都是最簡單的。由於整個邏輯都在組件內部,因此我們可以很容易地在組件加載後一次性獲取所有數據。

此方法的缺點

當進行基於數據的複雜交互時,問題就會出現。例如,考慮以下情況:

Async Operations in React Redux Applications

  • 我們不希望執行 JS 的線程被網絡請求阻塞。
  • 所有上述情況都會使代碼變得非常複雜,難以維護和測試。
  • 此外,可擴展性將是一個大問題,因為如果我們計劃更改應用程序的流程,我們需要從組件中刪除所有獲取操作。
  • 想像一下,如果組件位於父-子樹的頂部,我們會怎麼做。然後我們需要更改所有依賴於數據的表示組件。
  • 還要注意,整個業務邏輯都在組件內部。

我們如何改進?

  1. 狀態管理 在這些情況下,使用全局存儲實際上可以解決我們一半的問題。我們將使用 Redux 作為我們的全局存儲。

  2. 將業務邏輯移動到正確的位置 如果我們考慮將業務邏輯移出組件,那麼我們究竟可以在哪裡這樣做?在 actions 中?在 reducers 中?通過中間件? Redux 的架構是同步的。一旦你分發一個 action(JS 對象)並且它到達存儲,reducer 就會對其進行操作。

  3. 確保有一個單獨的線程執行異步代碼,並且可以通過訂閱檢索對全局狀態的任何更改

Async Operations in React Redux Applications

由此,我們可以了解到,如果我們將所有獲取邏輯移動到 reducer 之前——即 action 或中間件——那麼就可以在正確的時間分發正確的 action。例如,一旦獲取開始,我們可以分發 dispatch({ type: 'FETCH_STARTED' }),當它完成時,我們可以分發 dispatch({ type: 'FETCH_SUCCESS' })

想要開發一個 React JS 應用程序?

使用 Redux Thunk

Redux Thunk 是 Redux 的中間件。它基本上允許我們返回函數而不是對像作為 action。這通過提供 dispatchgetState 作為函數的參數來提供幫助。我們有效地使用 dispatch 在正確的時間分發必要的 actions。好處是:

  • 允許在函數內進行多次分發
  • 業務邏輯與獲取的關聯將從 React 組件移到 actions 中。

在我們的例子中,我們可以像這樣重寫 action:

userId = fetch(userEndPoint); // 从 userEndpoint 获取 userId
userDetails = fetch(userEndpoint, userId) // 为此特定 userId 获取数据。
登入後複製
登入後複製
登入後複製
登入後複製

如你所見,我們現在可以很好地控制何時分發哪種類型的 action。每個函數調用(如 fetchStarted()fetchUserDetailsSuccess()fetchRestaurantsSuccess()fetchError())都會分發一個類型為普通 JavaScript 對象的 action,如果需要,還可以添加其他詳細信息。因此,現在 reducer 的任務是處理每個 action 並更新視圖。我沒有討論 reducer,因為它從這裡開始很簡單,並且實現可能會有所不同。

為了使這能夠工作,我們需要將 React 組件與 Redux 連接起來,並使用 Redux 庫將 action 與組件綁定。一旦完成,我們可以簡單地調用 this.props.getRestaurants(),這反過來將處理所有上述任務並根據 reducer 更新視圖。

就其可擴展性而言,Redux Thunk 可用於不涉及對異步 action 進行複雜控制的應用程序。此外,它與其他庫無縫協同工作,如下一節主題中所述。

但是,使用 Redux Thunk 來執行某些任務仍然有點困難。例如,我們需要暫停中間的獲取操作,或者當有多個這樣的調用時,只允許最新的調用,或者如果其他 API 獲取此數據並且我們需要取消。

我們仍然可以實現這些,但是精確地執行會比較複雜。與其他庫相比,複雜任務的代碼清晰度會略差,並且維護起來會比較困難。

使用 Redux-Saga

使用 Redux-Saga 中間件,我們可以獲得額外的優勢來解決上述大多數功能。 Redux-Saga 是基於 ES6 生成器開發的。

Redux-Saga 提供了一個 API,有助於實現以下目標:

  • 阻塞事件,在同一行阻塞線程,直到完成某些操作
  • 非阻塞事件,使代碼異步
  • 處理多個異步請求之間的競爭
  • 暫停/節流/去抖動任何 action

Saga 如何工作?

Saga 使用 ES6 生成器和 async/await API 的組合來簡化異步操作。它基本上在其單獨的線程上執行其工作,我們可以在其中進行多個 API 調用。我們可以使用它們的 API 使每個調用同步或異步,具體取決於用例。 API 提供了使線程在同一行等待直到請求返迴響應的功能。除此之外,此庫還提供了許多其他 API,這使得 API 請求非常易於處理。

考慮我們之前的示例:如果我們初始化一個 saga 並根據其文檔中提到的內容將其與 Redux 配置,我們可以執行以下操作:

userDetails: {
  …
  city: 'city',
  …
};
登入後複製
登入後複製

因此,如果我們使用類型為 FETCH_RESTAURANTS 的簡單 action 進行分發,Saga 中間件將偵聽並響應。實際上,沒有一個 action 被中間件使用。它只是偵聽並執行一些其他任務,如果需要,則分發新的 action。通過使用此架構,我們可以分發多個請求,每個請求描述:

  • 第一個請求何時開始
  • 第一個請求何時完成
  • 第二個請求何時開始

等等。

此外,您可以看到 fetchRestaurantSaga() 的優點。我們目前使用 call API 來實現阻塞調用。 Saga 提供其他 API,例如 fork(),它實現非阻塞調用。我們可以組合阻塞和非阻塞調用來維護適合我們應用程序的結構。

就可擴展性而言,使用 saga 有益:

  • 我們可以根據任何特定任務來構建和分組 saga。我們可以通過簡單地分發 action 來從另一個 saga 觸發一個 saga。
  • 由於它是中間件,我們編寫的 action 將是普通的 JS 對象,與 thunk 不同。
  • 由於我們將業務邏輯放在 saga 內部(這是一個中間件),如果我們知道 saga 的功能是什麼,那麼理解它的 React 部分就會容易得多。
  • 可以通過 try/catch 模式輕鬆監控錯誤並將其分發到存儲。

使用 Redux-Observables

正如其文檔中“Epic 是 redux-observable 的核心原語”部分所述:

  1. Epic 是一個函數,它接收 action 流並返回 action 流。也就是說,Epic 與正常的 Redux 分發通道並行運行,在 reducer 已經接收它們之後。
  2. action 始終在 epic 接收它們之前通過 reducer 運行。 Epic 只接收並輸出另一個 action 流。這與 Redux-Saga 類似,因為沒有一個 action 被中間件使用。它只是偵聽並執行一些其他任務。

對於我們的任務,我們可以簡單地編寫如下代碼:

userId = fetch(userEndPoint); // 从 userEndpoint 获取 userId
userDetails = fetch(userEndpoint, userId) // 为此特定 userId 获取数据。
登入後複製
登入後複製
登入後複製
登入後複製

起初,這可能看起來有點令人困惑。但是,你越了解 RxJS,創建 Epic 就越容易。

與 saga 一樣,我們可以分發多個 action,每個 action 都描述線程當前位於 API 請求鏈的哪個部分。

就可擴展性而言,我們可以根據特定任務拆分 Epic 或組合 Epic。因此,此庫可以幫助構建可擴展的應用程序。如果我們理解編寫代碼的可觀察模式,則代碼清晰度很好。

我的偏好

如何確定使用哪個庫?這取決於我們的 API 請求有多複雜。

如何選擇 Redux-Saga 和 Redux-Observable 之間?這取決於學習生成器還是 RxJS。兩者都是不同的概念,但同樣足夠好。我建議嘗試兩者,看看哪個更適合你。

將處理 API 的業務邏輯放在哪裡?最好放在 reducer 之前,但不要放在組件中。最好的方法是在中間件中(使用 saga 或 observable)。

您可以在 Codebrahma 閱讀更多 React 開發文章。

關於 React-Redux 應用程序中異步操作的常見問題

中間件在處理 Redux 中的異步操作中的作用是什麼?

Redux 中的中間件在處理異步操作中起著至關重要的作用。它在分發 action 和 action 到達 reducer 之間提供了一個第三方擴展點。中間件可用於記錄、修改甚至取消 action,以及分發其他 action。在異步操作的上下文中,像 Redux Thunk 或 Redux Saga 這樣的中間件允許您編寫返回函數而不是 action 的 action 創建者。然後,可以使用此函數來延遲 action 的分發或僅在滿足特定條件時分發 action。

Redux Thunk 如何幫助管理異步操作?

Redux Thunk 是一個中間件,允許您編寫返回函數而不是 action 的 action 創建者。 thunk 可用於延遲 action 的分發或僅在滿足特定條件時分發 action。此功能使其成為處理 Redux 中異步操作的絕佳工具。例如,您可以分發一個 action 來指示 API 調用的開始,然後在調用返回數據或錯誤消息時分發另一個 action。

Redux Thunk 和 Redux Saga 之間的區別是什麼?

Redux Thunk 和 Redux Saga 都是用於管理 Redux 中副作用(包括異步操作)的中間件。兩者之間的主要區別在於它們的方法。 Redux Thunk 使用回調函數來處理異步操作,而 Redux Saga 使用生成器函數和更聲明式的方法。這使得 Redux Saga 更加強大和靈活,但也更加複雜。如果您的應用程序具有簡單的異步操作,Redux Thunk 可能就足夠了。但是,對於涉及競爭條件、取消和 if-else 邏輯的更複雜場景,Redux Saga 可能是更好的選擇。

如何在 Redux 中處理異步操作中的錯誤?

可以在異步操作期間發生錯誤時分發 action 來處理 Redux 中異步操作中的錯誤處理。此 action 可以將其錯誤消息作為其有效負載。然後,您可以在 reducer 中處理此 action 以使用錯誤消息更新狀態。這樣,可以向用戶顯示錯誤消息或將其記錄以進行調試。

如何在 Redux 中測試異步 action?

可以通過模擬 Redux 存儲和 API 調用來測試 Redux 中的異步 action。對於 Redux 存儲,您可以使用像 redux-mock-store 這樣的庫。對於 API 調用,您可以使用像 fetch-mocknock 這樣的庫。在您的測試中,您可以分發異步 action,然後斷言已使用正確的有效負載分發了預期的 action。

如何在 Redux 中取消異步操作?

可以使用像 Redux Saga 這樣的中間件來取消 Redux 中的異步操作。 Redux Saga 使用生成器函數,可以使用 cancel effect 來取消這些函數。當 cancel effect 被 yield 時,saga 將從其啟動點到當前 effect 被取消。

如何在 Redux 中處理異步操作中的競爭條件?

可以使用像 Redux Saga 這樣的中間件來處理 Redux 中異步操作中的競爭條件。 Redux Saga 提供了像 takeLatesttakeEvery 這樣的 effect,可用於處理並發 action。例如,如果在分發新 action 時,先前啟動的 saga 任務仍在運行,則 takeLatest 會取消該任務。

如何將 async/await 與 Redux Thunk 一起使用?

Redux Thunk 原生支持 async/await。在您的 action 創建者中,您可以返回異步函數而不是常規函數。在此異步函數內部,您可以使用 async/await 來處理異步操作。當異步操作完成時,可以使用 action 對象調用 dispatch 函數。

如何在 Redux 中的異步操作中處理加載狀態?

可以通過在異步操作之前和之後分發 action 來處理 Redux 中異步操作中的加載狀態。在操作之前分發的 action 可以將加載狀態設置為 true,而操作之後分發的 action 可以將其設置為 false。在您的 reducer 中,您可以處理這些 action 以更新存儲中的加載狀態。

如何在 Redux 中處理副作用?

可以使用像 Redux Thunk 或 Redux Saga 這樣的中間件來處理 Redux 中的副作用。這些中間件允許您編寫返回函數而不是 action 的 action 創建者。此函數可用於執行副作用,例如異步操作、記錄和有條件地分發 action。

以上是React Redux應用中的異步操作的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1321
25
PHP教程
1269
29
C# 教程
1249
24
JavaScript引擎:比較實施 JavaScript引擎:比較實施 Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

Python vs. JavaScript:學習曲線和易用性 Python vs. JavaScript:學習曲線和易用性 Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

JavaScript:探索網絡語言的多功能性 JavaScript:探索網絡語言的多功能性 Apr 11, 2025 am 12:01 AM

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

如何使用Next.js(前端集成)構建多租戶SaaS應用程序 如何使用Next.js(前端集成)構建多租戶SaaS應用程序 Apr 11, 2025 am 08:22 AM

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

使用Next.js(後端集成)構建多租戶SaaS應用程序 使用Next.js(後端集成)構建多租戶SaaS應用程序 Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

從C/C到JavaScript:所有工作方式 從C/C到JavaScript:所有工作方式 Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript和Web:核心功能和用例 JavaScript和Web:核心功能和用例 Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

JavaScript在行動中:現實世界中的示例和項目 JavaScript在行動中:現實世界中的示例和項目 Apr 19, 2025 am 12:13 AM

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

See all articles