如何编写第一个 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的原创文章,尽在:"汪子熙":