36

我正在开发一个使用 ngrx/store 1.5 和 thunk 中间件的应用程序,我正在尝试迁移到 ngrx/store 2.0 和 ngrx/effects。关于如何处理多个相关动作和/或效果,我有几个问题。

我意识到 thunk 与效果的“心态”是不同的,我正在努力解决这些差异。我查看了可用的示例应用程序,但没有发现任何似乎适合我正在尝试的东西,所以也许我仍然完全错误地接近它。

方案 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(...))以某种方式添加一些之外,我不确定还能做什么......

4

4 回答 4

20

导航场景的答案是你的 b 答案

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

说明:您对 LOGIN_SUCCESS 做出反应,并且由于路由器没有返回新操作,我们需要停止流的传播,我们通过过滤所有内容来完成。

如果忘记过滤,router会返回undefined,这反过来会导致reducer减少一个未定义的值,这通常会在它试图读取typeaction的时候导致一个空指针

另一种解决方法是使用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动作将分派路由器减速器将拾取并触发路由更改的路由器动作。

于 2016-06-21T06:22:06.250 回答
2

方案 1

考虑是否navigateHome应该改变状态。此外,此navigateHome操作是否从其他地方分派以实现相同的目的。如果是这样,返回一个动作是要走的路。因此,选项 A。

在某些情况下,选项 B 可能更有意义。如果navigateHome只是改变路线,可能值得考虑。

旁注:您可以在此处使用ignoreElements而不是filter(() => false).

方案 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
            });
        }
        // ...
    }
}
于 2016-06-28T09:06:48.833 回答
0

方案 1

我认为选项 A 和 B 都很好,但也许选项 A 对于导航来说有点太多了!LoginActions.LOGIN如果您将导航视为此副作用的一部分,您也可以直接使用路由器。

方案 2

上链并没有错@Effects。调度一个动作(SET_1_REQUEST)来触发你的效果A。效果A返回一个动作(SET_1_SUCCESS),它被你的reducer拾取(现在你可以向用户显示该状态)并且它被效果B拾取。

希望这是有道理的。

于 2016-06-21T06:21:46.917 回答
-1

我也在学习这个东西。

如果你在链中的第一个完成时分派到减速器怎么办?

像这样的东西:

const myReducer = (state,action:Action) => {
switch action.type {
case : "my_first_step" : runfirststep(action.payload);
case : "my_second_step": runsecondstep(action.payload);
....

function runfirststep(data) {
...
//bunch of stuff to do,
//update something to display
store.dispatch('my_second_step');
}

等等

ngrx 示例在效果文件中执行此操作。当添加或更新时,他们可能会调用 UPDATE_COMPLETE 或类似的东西,以便可以完成一些通知。

我正在慢慢掌握减速器最终会出现在一个中等复杂的应用程序中的大小。

于 2016-06-19T02:04:20.917 回答