如何編寫第一個 ngrx Effect 類
要將副作用與您的元件隔離,您必須建立一個 Effects 類來偵聽事件並執行任務。
Effect 是具有不同部分的可注入服務類:
- 一個可注入的 Actions 服務,它提供了在 reduce 最新狀態後排程的所有操作的可觀察流。
如下圖所示:
-
使用 createEffect 函式將元資料附加到可觀察流。 元資料用於註冊訂閱儲存的流。從 effect 流返回的任何操作都會被分派回 Store。
-
使用可管道化的 ofType 運算子過濾操作。 ofType 運算子將一種或多種操作型別作為引數來過濾要執行的操作。
如下圖所示:
-
effects 訂閱了 Store observable.
-
服務被注入到效果中以與外部 API 互動並處理流。
看一個實際的 effects 實現例子:
import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { EMPTY } from 'rxjs'; import { map, mergeMap, catchError } from 'rxjs/operators'; import { MoviesService } from './movies.service'; @Injectable() export class MovieEffects { loadMovies$ = createEffect(() => this.actions$.pipe( ofType('[Movies Page] Load Movies'), mergeMap(() => this.moviesService.getAll() .pipe( map(movies => ({ type: '[Movies API] Movies Loaded Success', payload: movies })), catchError(() => EMPTY) )) ) ); constructor( private actions$: Actions, private moviesService: MoviesService ) {} }
loadMovies$ 效果通過 Actions 流監聽所有 dispatch 的 action,但只對使用 ofType 操作符的 [Movies Page] Load Movies 事件感興趣。
我們必須使用 ofType 來過濾事件,應該通過建構函式依賴注入得到的 action 例項是一個單例,預設會捕捉到系統所有 dispatch 的事件。
然後使用 mergeMap 運算子將 action 流展平,並對映到新的可觀察物件中。 MoviesService#getAll() 方法返回一個 observable,該 observable 將電影對映到成功的新 action,如果發生錯誤,當前返回一個空的 observable。
當需要更改狀態時,action 被分派到 Store,在那裡它可以由 reducer 處理。 在處理可觀察流時處理錯誤也很重要,這樣 effect 才能繼續執行。
Registering root effects
編寫 Effects 類後,必須註冊它,以便效果開始執行。 要註冊根級 effects,請將 EffectsModule.forRoot() 方法與您的 effect 陣列新增到您的 AppModule。
下面是 app.module.ts 的例子:
import { EffectsModule } from '@ngrx/effects';
import { MovieEffects } from './effects/movie.effects';
@NgModule({
imports: [
EffectsModule.forRoot([MovieEffects])
],
})
export class AppModule {}
即使您沒有註冊任何根級效果,也必須將 EffectsModule.forRoot() 方法新增到您的 AppModule 匯入中。
下面的程式碼來自 Spartacus 的 app.module.ts:
效果在 AppModule 載入後立即開始執行,以確保它們儘快偵聽所有相關操作。
Registering feature effects
對於功能模組,通過在 NgModule 的匯入陣列中新增 EffectsModule.forFeature() 方法來註冊你的 effect。
例子:
import { EffectsModule } from '@ngrx/effects';
import { MovieEffects } from './effects/movie.effects';
@NgModule({
imports: [
EffectsModule.forFeature([MovieEffects])
],
})
export class MovieModule {}
通過 forRoot() 或 forFeature() 多次執行效果類(例如通過不同的延遲載入模組)不會導致效果多次執行。 forRoot() 和 forFeature() 載入的效果之間沒有功能差異; 函式之間的重要區別在於 forRoot() 設定 Effects 所需的 providers 程式。
更多Jerry的原創文章,盡在:"汪子熙":