web-dev-qa-db-ja.com

ngrx / effectsを使用して複数の関連する操作/効果/アクションを実行する方法

サンクミドルウェアとともにngrx/store 1.5を使用しているアプリケーションで作業しており、ngrx/store 2.0およびngrx/effectsに移行しようとしています。複数の関連するアクションや効果を処理する方法に関して、いくつか質問があります。

サンクとエフェクトの「考え方」が異なることを認識しており、違いを回避しようとしています。私は利用可能なサンプルアプリを調べましたが、私がしようとしているものに合うと思われるものを見つけていないので、おそらく私はまだ完全に間違っています。

シナリオ1

ログインのサーバーへのリクエストを処理する副作用は次のとおりです。

_@Effect login$: any = this.updates$
    .whenAction(LoginActions.LOGIN)
    .map(toPayload)
    .switchMap(payload => 
        this.loginService.login(payload.user, payload.password)
            .map(result => this.actions.loginSuccess(value))
            .catch((error) => Observable.of(this.loginError(error)))
));
_

その最初の副作用を考えると、ログインの成功時に「ホーム」画面へのナビゲーションをトリガーする「正しい」または「推奨される」方法は何でしょうか?これは、アクションまたは操作のシーケンスを単にトリガーするために一般化することもできます。

私が検討したいくつかのオプション:

(a)ログインの成功によってトリガーされる別の効果。ナビゲーションをトリガーする後続のアクションを起動しますか?

_@Effect navigateHome$: any = this.updates$
    .whenAction(LoginActions.LOGIN_SUCCEEDED)
    .mapTo(this.actions.navigateHome());
_

(b)ログインの成功によってトリガーされる別の効果は、単にナビゲーション操作を実行しますか?

_@Effect navigateHome$: any = this.updates$
    .whenAction(LoginActions.LOGIN_SUCCEEDED)
    .do(this.navigateHome())
    .filter(() => false);
_

(c)追加のアクションを、最初のログイン効果によって発行されたアクションに連結しますか? (サンプルは明らかに完全に正しくありませんが、アイデアを与えます)

_@Effect login$: any = this.updates$
    .whenAction(LoginActions.LOGIN)
    .map(toPayload)
    .switchMap(password => Observable.concat(
        this.loginService.login(passcode)
            .map(result => this.actions.loginSuccess(value))
            .catch((error) => Observable.of(this.loginError(error))),
        Observable.of(this.actions.navigateHome())
    ));
_

(d)その他?

シナリオ2

多数の要求を順番に行う必要があり、各要求が開始されると、ユーザーにフィードバックを提供できるように「ステータス」を更新する場合を考えます。

これらの線に沿った何かのサンクの例:

_multiphaseAction() {
    return (dispatch) => {
        dispatch(this.actions.updateStatus('Executing phase 1');
        this.request1()
            .flatMap(result => {
                dispatch(this.actions.updateStatus('Executing phase 2');
                return this.request2();
            })
            .flatMap(result => {
                dispatch(this.actions.updateStatus('Executing phase 3');
                return this.request3();
            })
            ...
    }
}
_

繰り返しますが、効果アプローチを使用してこれを実行するための「正しい」または「推奨される」方法は何ですか?

これは私がもっと固執しています、何らかの方法で.do(this.store.dispatch(this.actions.updateStatus(...))を追加する以外に何ができるか本当にわかりません...

34

ナビゲーションシナリオの答えは、あなたのb答えです

@Effect navigateHome$: any = this.updates$
    .whenAction(LoginActions.LOGIN_SUCCEEDED)
    .do(this.router.navigate('/home'))
    .ignoreElements();

説明:LOGIN_SUCCESSに反応します。ルーターは新しいアクションを返さないため、すべてのフィルタリングによってストリームの伝播を停止する必要があります。

フィルタリングを忘れると、ルーターは未定義を返します。これにより、リデューサーは未定義の値を削減します。通常、アクションのtypeを読み取ろうとすると、nullポインターになります。

それを解決する別の方法は、 https://github.com/ngrx/router-store を使用することです

アプリにルーターストアを追加する方法に関するドキュメントを確認してください。

同じ効果は、このようになります。

import { go } from '@ngrx/router-store';

@Effect navigateHome$: any = this.updates$
    .whenAction(LoginActions.LOGIN_SUCCEEDED)
    .map(() => go(['/home']));

goアクションはルーターアクションをディスパッチし、ルーターレデューサーがそれを取得してルート変更をトリガーします。

17
Leon Radley

シナリオ1

navigateHomeが状態を変更する必要があるかどうかを検討します。また、このnavigateHomeアクションが同じことを達成するために他の場所からディスパッチされるかどうか。もしそうなら、アクションを返すことは行く方法です。したがって、オプションA。

場合によっては、オプションBの方が意味があります。 navigateHomeがルートのみを変更する場合、検討する価値があるかもしれません。

補足:filter(() => false)の代わりに ignoreElements を使用できます。

シナリオ2

ここでアクションを複数のエフェクトにチェーンし、アクションごとにリデューサーの状態を変更してフィードバックを与えることをお勧めします。

例えば:

@Effect() trigger$: Observable<Action> = this.updates$
    .whenAction(Actions.TRIGGER)
    .do(() => console.log("start doing something here"))
    .mapTo(Actions.PHASE1);

@Effect() phase1$: Observable<Action> = this.updates$
    .whenAction(Actions.PHASE1)
    .do(() => console.log("phase 1"))
    .mapTo(Actions.PHASE2);

@Effect() phase2$: Observable<Action> = this.updates$
    .whenAction(Actions.PHASE2)
    .do(() => console.log("phase 2"))
    .ignoreElements();

状態を変更してフィードバックを提供します。

function reducer(state = initialState, action: Action): SomeState {
    switch (action.type) {
        case Actions.TRIGGER: {
            return Object.assign({}, state, {
                triggered: true
            });
        }
        case Actions.PHASE1: {
            return Object.assign({}, state, {
                phase1: true
            });
        }
        case Actions.PHASE2: {
            return Object.assign({}, state, {
                phase1: false,
                phase2: true
            });
        }
        // ...
    }
}
2
Laurens

シナリオ1

オプションAとBは両方とも良いと思いますが、オプションAはナビゲーションだけでは多すぎるかもしれません!この副作用の一部としてナビゲーションを考慮する場合は、LoginActions.LOGINでルーターを直接使用することもできます。

シナリオ2

@Effectsのチェーンアップに問題はありません。エフェクトAをトリガーするアクション(SET_1_REQUEST)をディスパッチします。エフェクトAは、リデューサーによって取得されるアクション(SET_1_SUCCESS)を返し(現在、そのステータスをユーザーに表示できます)、エフェクトBによって取得されます。

これが理にかなっていることを願っています。

0
littleStudent