web-dev-qa-db-ja.com

@ ngrx / effectsで2つのアクションを待機する方法

Promise.allのような2つのアクションを待機できますか?例:

@Effect()
pulic addUser() {
   return this.actions$.ofType(user.ADD)
      .switchMap(() => {
         return this.userService.add();
      })
      .map(() => {
         return new user.AddSuccessAction();
      });
}

@Effect()
pulic addUserOptions() {
   return this.actions$.ofType(userOptions.ADD)
      .switchMap(() => {
         return this.userOptionsService.add();
      })
      .map(() => {
         return new userOptions.AddSuccessAction();
      });
}

@Effect()
public complete() {
   return this.actions$.ofType(user.ADD_SUCCESS, userOptions.ADD_SUCCESS)
      // how to make it works like Promise.all ?
      .switchMap(() => {
         return this.statisticService.add();
      })
      .map(() => {
         return new account.CompleteAction();
      });
}

[〜#〜]更新[〜#〜]私が達成したいのは、Promise.allに対する類似した動作です。 2つのエフェクトを並行してディスパッチする方法。すべてのエフェクトが解決されるまで待ってから、3番目のアクションをディスパッチします。 のようなものhttps://redux-saga.js.org/docs/advanced/RunningTasksInParallel.html promiseを使用すると、それは非常に明白でした:

Promise.all([fetch1, fetch2]).then(fetch3);

Ngrx /エフェクトで可能ですか?それとも、ngrx/effectsでは間違った方法ですか?

[〜#〜]回答[〜#〜]

使用できるオプションはいくつかあります。

1)一般的なアクションは使用しないでください。

Myke Ryanのプレゼンテーションから次のルールに従ってください: https://youtu.be/JmnsEvoy-gY

長所:デバッグが容易

短所:大量のボイラープレートとアクション

2)ネストされたアクションで複雑なストリームを使用します。

この記事を確認してください: https://bertrandg.github.io/ngrx-effects-complex-stream-with-nested-actions/

2つのアクションの簡単な例を次に示します。

@Effect()
public someAction(): Observable<Action> {
    return this.actions$.pipe(
        ofType(actions.SOME_ACTION),
        map((action: actions.SomeAction) => action.payload),
        mergeMap((payload) => {
            const firstActionSuccess$ = this.actions$.pipe(
                ofType(actions.FIRST_ACTION_SUCCESS),
                takeUntil(this.actions$.pipe(ofType(actions.FIRST_ACTION_FAIL))),
                first(),
            );

            const secondActionsSuccess$ = this.actions$.pipe(
                ofType(actions.SECOND_ACTION_SUCCESS),
                takeUntil(this.actions$.pipe(ofType(actions.SECOND_ACTION_FAIL))),
                first(),
            );

            const result$ = forkJoin(firstActionSuccess$, secondActionsSuccess$).pipe(
                first(),
            )
                .subscribe(() => {
                    // do something
                });

            return [
                new actions.FirstAction(),
                new actions.SecondAction(),
            ];
        }),
    );
}

長所:あなたが望むものを達成することができます

短所:複雑なストリームは複雑すぎてサポートできません:)見た目が醜く、すぐに地獄になる可能性があります。理論的には、サードパーティのアクションがこれらのオブザーバブルにシグナルを送信できることを意味します。

3)アグリゲータパターンを使用します。

NgRxを使用した状態管理パターンとベストプラクティスに関するVictor Savkinのプレゼンテーションを確認してください: https://www.youtube.com/watch?v=vX2vG0o-rpM

ここに簡単な例があります:

最初に、correlationId paramを使用してアクションを作成する必要があります。 CorrelationIdはuniqである必要があります。たとえば、いくつかのGUIDです。このIDは、アクションを識別するためにアクションのチェーンで使用します。

export class SomeAction implements Action {
    public readonly type = SOME_ACTION;

    constructor(public readonly correlationId?: string | number) { }
    // if you need payload, then make correlationId as a second argument
    // constructor(public readonly payload: any, public readonly correlationId?: string | number) { }
}

export class SomeActionSuccess implements Action {
    public readonly type = SOME_ACTION_SUCCESS;

    constructor(public readonly correlationId?: string | number) { }
}

export class FirstAction implements Action {
    public readonly type = FIRST_ACTION;

    constructor(public readonly correlationId?: string | number) { }
}

export class FirstActionSuccess implements Action {
    public readonly type = FIRST_ACTION_SUCCESS;

    constructor(public readonly correlationId?: string | number) { }
}

// the same actions for SecondAction and ResultAction

次に、効果:

@Effect()
public someAction(): Observable<Action> {
    return this.actions$.pipe(
        ofType(actions.SOME_ACTION),
        mergeMap((action: actions.SomeAction) => {
            return [
                new actions.FirstAction(action.corelationId),
                new actions.SecondAction(action.corelationId),
            ];
        }),
    );
}

@Effect()
public firstAction(): Observable<Action> {
    return this.actions$.pipe(
        ofType(actions.FIRST_ACTION),
        switchMap((action: actions.FirstAction) => {
            // something
            ...map(() => new actions.FirstActionSuccess(action.correlationId));
        }),
    );
}
// the same for secondAction

@Effect()
public resultAction(): Observable<Action> {
    return this.actions$.pipe(
        ofType(actions.SOME_ACTION),
        switchMap((action: actions.SomeAction) => {
            const firstActionSuccess$ = this.actions$.pipe(
                ofType(actions.FIRST_ACTION_SUCCESS),
                filter((t: actions.FirstActionSuccess) => t.correlationId === action.correlationId),
                first(),
            );

            const secondActionsSuccess$ = this.actions$.pipe(
                ofType(actions.SECOND_ACTION_SUCCESS),
                filter((t: actions.SecondActionSuccess) => t.correlationId === action.correlationId),
                first(),
            );

            return Zip(firstActionSuccess$, secondActionsSuccess$).pipe(
                map(() => new actions.resultSuccessAction()),
            )
        }),
    );
}

長所:ポイント2と同じですが、サードパーティのアクションはありません。

短所:ポイント1および2と同じ

4)APIにエフェクトを使用しないでください。効果をエミュレートするがObservableを返す古き良きサービスを使用します。

あなたのサービス:

public dispatchFirstAction(): Observable<void> {
    this.store.dispatch(new actions.FirstAction(filter));

    return this.service.someCoolMethod().pipe(
        map((data) => this.store.dispatch(new actions.FirstActionSuccess(data))),
        catchError((error) => {
            this.store.dispatch(new actions.FirstActionFail());

            return Observable.throw(error);
        }),
    );
}

したがって、後でどこでも組み合わせることができます。

const result1$ = this.service.dispatchFirstAction();
const result2$ = this.service.dispatchSecondAction();

forkJoin(result1$, result2$).subscribe();

5)ngxsを使用: https://github.com/ngxs/store

長所:ボイラープレートが少なく、これはangularもののように感じられ、急速に成長します

短所:ngrxよりも機能が少ない

19
E. Efimov

RXJSは初めてですが、これについてはどうですか。

削除できます{dispatch: false}tapswitchMapに変更した場合。

@Effect({dispatch: false})
public waitForActions(): Observable<any> {
    const waitFor: string[] = [
        SomeAction.EVENT_1,
        SomeAction.EVENT_2,
        SomeAction.EVENT_3,
    ];

    return this._actions$
        .pipe(
            ofType(...waitFor),
            distinct((action: IAction<any>) => action.type),
            bufferCount(waitFor.length),
            tap(console.log),
        );
}
7
codeBelt

Observable.combineLatest を使用するとうまくいきます。

@Effect()
  complete$ = this.actions$.ofType<Action1>(ACTION1).combineLatest(this.actions$.ofType<Action2>(ACTION2),
    (action1, action2) => {

      return new Action3();
    }
  ).take(1);

take(1)を実行すると、Action3()が1回だけディスパッチされます。

4
deumax

combineLatestpipesを含む別のswitchMapバージョン

import { Observable, of } from 'rxjs'
import { combineLatest, switchMap, withLatestFrom } from 'rxjs/operators'

@Effect()
someEffect$: Observable<Actions> = this.actions$.pipe(
  ofType(Action1),
  combineLatest(this.actions$.ofType(Action2)),
  switchMap(() => of({ type: Action3 }))
)
1