ngrx链接动作/效果 - 例如登录然后导航

dan*_*nym 11 ngrx ngrx-effects angular

我是ngrx和Redux风格架构的新手,我在理解如何链接动作/效果方面遇到了问题.一个例子是实现基本功能,例如在用户登录后采取行动.我的核心斗争是在用户登录后要采取的行动将根据应用程序的当前状态而变化 - 用户可能在任何地方在提交登录提示/页面时在应用程序中.

我见过的任何一些例子,一旦用户登录就会发生硬编码效果.在我的场景中,这不符合上述说法,我并不总是希望同样的动作发生.

以下是包含登录组件的主页的一些示例代码.在这种情况下,我希望用户在登录后重定向到"/ buy".

@Component(..)
export class HomePageComponent {
    constructor(private store: Store<any>) {
    }

    public onLoginSubmitted(username, password) {
        this.store.dispatch(new loginActions.Login({username, password}));
        // once this has happened successfully navigate to /buy    
    }

}
Run Code Online (Sandbox Code Playgroud)

示例效果

@Injectable()
export class LoginEffects {

    ......     

    @Effect()
    login$ = this.actions$
        .ofType(loginActions.ActionTypes.LOGIN)
        .switchMap((data) => {
            return this.loginService.login(data.payload)
                .map(() => new loginActions.LoginSuccess({loggedIn: true, isLoggingIn: false}))
                .catch((error) => {
                    return of(new loginActions.LoginError({loggedIn: false, isLoggingIn: false}));
                });
        });
    ......
}
Run Code Online (Sandbox Code Playgroud)

关于如何解决这个问题,我有几点想法 - 但他们都不是正确的.这些包括

  • 使用登录有效负载传递数据以确定下一步要采取的操作
  • 写一些在LoginSuccess上采取行动但在某个更高级别过滤的效果
  • 通过监听商店中的事件来编写特定于组件的效果(这是可能/不好的做法吗?)
  • 从商店读取有关当前登录状态的数据并在组件中对此进行操作.然而,这种逻辑会立即运行,这可能不会产生预期的效果

任何人都可以指出我应该如何解决这个问题的正确途径/现有例子吗?

Ada*_*dam 10

效果可以使用mergeMap一系列动作触发多个动作.我通常会做的是,传递我希望登录效果也发送的其他操作.

@Component(..)
export class HomePageComponent {
    constructor(private store: Store<any>) { }

    public onLoginSubmitted(username, password) {
        this.store.dispatch(new loginActions.Login({
            username, 
            password, 
            onCompleteActions: [new loginActions.Redirect({ route: '/buy' })] }));
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,登录效果将使用分派任何传入的操作mergeMap.您仍然需要为每个操作创建一个效果,但现在您可以构建更多可重用的操作,例如重定向操作.

@Injectable()
export class LoginEffects {

    ......     

    @Effect()
    login$ = this.actions$
        .ofType(loginActions.ActionTypes.LOGIN)
        .switchMap((data) => {
            let mappedActions = [new loginActions.LoginSuccess({loggedIn: true, isLoggingIn: false})];
            if (data.payload.onCompleteActions)
                mappedActions = mappedActions.concat(data.payload.onCompleteActions);

            return this.loginService.login(data.payload)
                .mergeMap(mappedActions)
                .catch((error) => {
                    return of(new loginActions.LoginError({loggedIn: false, isLoggingIn: false}));
                });
        });

    ......
}
Run Code Online (Sandbox Code Playgroud)

我喜欢这个解决方案的是它可以在未来扩展,因为任何动作都可以传入,并且没有switch或if-else树来决定做什么.您也可以通过在多个动作,所以没有必要建立一个"redirectAndCloseModal"动作,你可以传递redirect以及closeModal.

this.store.dispatch(new loginActions.Login({
    username, 
    password, 
    onCompleteActions: [new loginActions.Redirect({ route: '/buy' }), new modalActions.CloseModal()] }));
Run Code Online (Sandbox Code Playgroud)

您可以在此github问题上阅读更多关于从一个@Effect()调度多个Actions的内容.

编辑:关于你的评论,不仅仅是路线变化可能会让你想要停止登录,它可能是任何东西,比如关闭一个模态,并且没有通用的方法来观察.您可以在商店中添加一些内容,例如runLoginActions布尔值,然后在登录请求返回后从商店中读取该数据.

然后在您的login$效果中,您可以从商店中读取该内容以决定是否运行其他操作.

@Effect()
login$: Observable<Action> = this.actions$
    .ofType(auth.actionTypes.LOGIN)
    .map((action: auth.LoginAction) => action.payload)
    .switchMap((user) => {
        let successAction = new loginActions.LoginSuccess({loggedIn: true, isLoggingIn: false});
        let mappedActions = [successAction];
        if (data.payload.onCompleteAction)
            mappedActions.push(data.payload.onCompleteAction);

        return this.authService.login(user.username, user.password)
            .withLatestFrom(this.store)
            .mergeMap(([result, store]) => store.runLoginActions ?
                [mappedActions] :
                [successAction]
            )
            .catch((err) => {
                try {
                    return of(new auth.NetworkFailureAction(err[0].code));
                } catch (e) {
                    throw `Array not in the expected format of [{ code: "", message: "" }]`;
                }
            });
    });
Run Code Online (Sandbox Code Playgroud)

请注意,在reducer中loginSuccess,您应该设置runLoginActions = true,并且默认情况下它应该从true开始,仅在路径更改或组件被销毁时关闭.

请记住,这是一个通用解决方案,您需要自己添加操作,更新减速器等.

这是一个更多的手动过程,但由于您不想执行操作的情况非常具体,因此可能必须以这种方式完成.