Подтвердить что ты не робот

Как выполнить несколько связанных операций/эффектов/действий с помощью ngrx/effects

Я работаю над приложением, использующим ngrx/store 1.5 вместе с промежуточным программным обеспечением thunk, и я пытаюсь перейти к 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(...)) как-то...

4b9b3361

Ответ 1

Ответ для сценария навигации - ваш ответ b

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

Объяснение: Вы реагируете на LOGIN_SUCCESS, и поскольку маршрутизатор не возвращает новое действие, нам нужно остановить распространение потока, что мы делаем, фильтруя все.

Если вы забудете фильтровать, маршрутизатор возвращает undefined, что, в свою очередь, приведет к сокращению, чтобы уменьшить значение undefined, которое обычно приводит к нулевому указателю, когда он пытается прочитать type действие

Другим способом решения этой проблемы является использование 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 будет отправлено действие маршрутизатора, которое редуктор маршрутизатора поднимет и вызовет изменение маршрута.

Ответ 2

Сценарий 1

Учитывайте, должно ли /t 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
            });
        }
        // ...
    }
}

Ответ 3

Сценарий 1

опции A и B оба хороши, я думаю, но, возможно, вариант A слишком много для навигации! Вы также можете напрямую использовать маршрутизатор в своем LoginActions.LOGIN, если вы рассматриваете навигацию как часть этого побочного эффекта.

Сценарий 2

Нет ничего плохого в цепочке @Effects. Отправляйте действие (SET_1_REQUEST), которое запускает эффект A. Эффект A возвращает действие (SET_1_SUCCESS), которое получает ваш редуктор (теперь вы можете отобразить этот статус для пользователя), и он получает результат с помощью эффекта B.

Надеюсь, что это имеет смысл.

Ответ 4

Я тоже изучаю этот материал.

Что делать, если вы отправили на редуктор, когда первый в цепочке был выполнен?

Что-то вроде этого:

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 или что-то подобное, предположительно, поэтому может быть сделано некоторое уведомление.

Я медленно понимаю, насколько большие редукторы окажутся в умеренно сложном приложении.