@ngrx/effectsを使ってみる
はじめに
昔@ngrx/store
についてまとめたから、今回は@ngrx/effects
についてまとめる。
インストールしてサクッと動かしてみるとこまで。
TL;DR.
@ngrx/effectsとは
APIアクセスなどの副作用のある処理をラップしてくれるもの。
前提条件
(なくてもいいけど、)前回の状態。
インストール
$ npm install --save @ngrx/effects
Service作成
ここは普通のAngularと同じく、API叩くServiceを作成する。
ただ、今回は 作るのがめんどくさかったから サクッと作るためObservable生成して流すことにする。
@Injectable({ providedIn: 'root' }) export class TodoService { constructor(private http: HttpClient) { } public fetchAll(): Observable<Array<string>> { return Observable.create(observable => { observable.next(['task1', 'task2', 'task3']); observable.complete(); return observable; }); } }
Action作成
サービスを取得するためのActionを作成する。
前回の状態があるなら、todo.action.ts
に追記でOK。
import { Action } from '@ngrx/store'; export enum ActionTypes { Fetch = 'TODO_FETCH', Load = 'TODO_LOAD' } export class Fetch implements Action { readonly type = ActionTypes.Fetch; }
Reducer作成
取得結果を処理する。
結果を受け取ってリストをまるっと入れ替えてあげる。
Actionと同じく todo.reducer.ts
に記載。
import { Action } from '@ngrx/store'; import { ActionTypes } from '../action/todo.action'; export const initialList: Array<string> = []; export function todoReducer(state = initialList, action: Action): Array<string> { switch (action.type) { case ActionTypes.Fetch: return [...action['payload']]; default: return state; } }
Effects作成
serviceをラップして作ってあげる様に作ってあげる。
名前はtodo.effects.ts
とする。
import {Injectable} from '@angular/core'; import {Actions, Effect, ofType} from '@ngrx/effects'; import {EMPTY} from 'rxjs'; import {catchError, map, mergeMap} from 'rxjs/operators'; import {TodoService} from '../service/todo.service'; import {ActionTypes, Fetch} from '../action/todo.action'; @Injectable() export class TodoEffects { @Effect() loadTodo$ = this.actions$ .pipe( // このイベントが発火するためのAction ofType(ActionTypes.Load), // サービス呼び出す mergeMap(() => this.todoService.fetchAll() .pipe( map(serverTodo => { // serviceの結果を元にAction発火 return new Fetch({todos: serverTodo}); }), catchError(() => EMPTY) )) ); constructor( private actions$: Actions, private todoService: TodoService ) { } }
moduleに追記
effectsを使うためにmoduleに追記する。
@NgModule({ declarations: [ AppComponent ], imports: [ // 配列形式でEffects定義。 EffectsModule.forRoot([TodoEffects]) ], providers: [TodoService], bootstrap: [AppComponent] }) export class AppModule { }
Componentに定義
componentから呼び出すための設定。
今回は初期表示時に設定した内容がほしいので、OnInit → effectsのイベント発火 → 結果をsubscribe。
という流れで設定。
@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { public todo$: Observable<Array<string>>; public todolist: Array<string> = []; constructor( private store: Store<{ todo: Array<string>}>, private service: TodoService ) { this.todo$ = store.pipe(select('todo')); this.todo$.subscribe(res => this.todolist = res); } ngOnInit(): void { // 初期表示用にイベント発火 this.store.dispatch({ type: ActionTypes.Load }); } }
動かしてみる
ng serve
で起動。
初期表示がされてればOK!
まとめ
今回はサクッとeffectsを使ってみた。
ドキュメントのコードを丸コピしてたんだけど、
effectsからReducerに通るところでハマってた。
ちゃんと文章読むのは大事だねというところで、今回はこのへんで。