Эффект ngrx не вызывается, когда действие отправляется из компонента

У меня возникла проблема с хранилищем ngrx, не отправляющим действие, которое, как предполагается, имеет дело с ним.

Вот компонент, который пытается отправить:

  signin() {
    this.formStatus.submitted = true;
    if (this.formStatus.form.valid) {
      this.store.dispatch(new StandardSigninAction(this.formStatus.form.value.credentials));
    }
  }

Действия:

export const ActionTypes = {
  STANDARD_SIGNIN: type('[Session] Standard Signin'),
  LOAD_PERSONAL_INFO: type('[Session] Load Personal Info'),
  LOAD_USER_ACCOUNT: type('[Session] Load User Account'),
  RELOAD_PERSONAL_INFO: type('[Session] Reload Personal Info'),
  CLEAR_USER_ACCOUNT: type('[Session] Clear User Account')
};

export class StandardSigninAction implements Action {
  type = ActionTypes.STANDARD_SIGNIN;

  constructor(public payload: Credentials) {
  }
}
...

export type Actions
  = StandardSigninAction
  | LoadPersonalInfoAction
  | ClearUserAccountAction
  | ReloadPersonalInfoAction
  | LoadUserAccountAction;

Эффект:

  @Effect()
  standardSignin$: Observable<Action> = this.actions$
    .ofType(session.ActionTypes.STANDARD_SIGNIN)
    .map((action: StandardSigninAction) => action.payload)
    .switchMap((credentials: Credentials) =>
      this.sessionSigninService.signin(credentials)
        .map(sessionToken => {
          return new LoadPersonalInfoAction(sessionToken);
        })
    );

В отладке я вижу, что компонент вызывает метод отправки. Я также могу подтвердить, что StandardSigninAction действительно создается, потому что точка останова в конструкторе попадает.

Но эффект standardSignin$ не вызывается...

Что может вызвать вызванный эффект?

Как я могу отладить, что происходит в магазине?

Кто-нибудь может помочь?

P.S. Я выполняю вышеупомянутый эффект следующим образом в моем импорте:

EffectsModule.run(SessionEffects),

изменить. Вот мой метод SessionSigninService.signin(возвращает возвращаемое значение)

  signin(credentials: Credentials) {
    const headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'});
    const options = new RequestOptions({headers: headers});
    const body = 'username=' + credentials.username + '&password=' + credentials.password;
    return this.http.post(this.urls.AUTHENTICATION.SIGNIN, body, options).map(res => res.headers.get('x-auth-token'));
  }

Ответ 1

Это не будет окончательный ответ, но, надеюсь, это будет полезно.

Прежде чем начать:

  • Убедитесь, что вы используете последние версии пакетов @ngrx (которые подходят для используемой версии Angular).
  • Если вы обновили какие-либо пакеты, убедитесь, что вы заново запустили среду разработки (то есть перезагрузите узел, сервер и т.д.).

Если вы еще этого не сделали, вы должны взглянуть на реализацию Store - чтобы вы сделали некоторые образованные догадки о том, что может пойти не так. Заметьте, что Store довольно светлый. Это как наблюдаемое (использующее состояние как его источник), так и наблюдатель (который подчиняется диспетчеру).

Если вы посмотрите store.dispatch, вы увидите, что это псевдоним для store.next, который вызывает next на Dispatcher.

Поэтому вызов:

this.store.dispatch(new StandardSigninAction(this.formStatus.form.value.credentials));

следует просто увидеть действие, испущенное диспетчером.

Наблюдаемый Actions, который вводится в ваши эффекты, также довольно легкий. Это просто наблюдаемое, которое использует Dispatcher в качестве источника.

Чтобы посмотреть на действия, протекающие через эффект, вы можете заменить это:

@Effect()
standardSignin$: Observable<Action> = this.actions$
  .ofType(session.ActionTypes.STANDARD_SIGNIN)

с этим:

@Effect()
standardSignin$: Observable<Action> = this.actions$
  .do((action) => console.log(`Received ${action.type}`))
  .filter((action) => action.type === session.ActionTypes.STANDARD_SIGNIN)

ofType не является оператором; это метод, поэтому для добавления журнала do, его нужно заменить на filter.

При входе в систему, если вы получаете действие, что-то не так с реализацией эффекта (или, возможно, строки/константы типов действий не то, что вы думаете, а что-то несовместимо).

Если эффект не получает отправленное действие, наиболее вероятным объяснением было бы то, что Store, через которое вы отправляете StandardSigninAction, не тот самый Store, который использует ваш эффект, - то есть вы имеют проблему с DI.

Если это так, вы должны посмотреть на то, что отличается от другого SessionEffects, который, как вы говорите, работает. (По крайней мере, у вас есть что-то работающее, что является хорошим местом для начала экспериментов.) Отправляются ли они из другого модуля? Модуль, который отправляет StandardSigninAction функциональный модуль?

Что произойдет, если вы взломаете один из рабочих SessionEffects, чтобы заменить его отправленное действие на StandardSigninAction? Выполняется ли эффект?

Обратите внимание, что вопросы в конце этого ответа не являются вопросами, на которые я хочу ответить; это вопросы, которые вы должны задавать себе и исследовать.

Ответ 2

Поток вашего хранилища может останавливаться из-за необработанных ошибок или, возможно, более смутных ошибок, которые, как представляется, обрабатываются с помощью .catch, которые фактически убивают поток без повторного использования нового Observable для продолжения.

Например, это приведет к уничтожению потока:

this.actions$
    .ofType('FETCH')
    .map(a => a.payload)
    .switchMap(query => this.apiService.fetch$(query)
        .map(result => ({ type: 'SUCCESS', payload: result }))
        .catch(err => console.log(`oops: ${err}`))) // <- breaks stream!

Но это сохранит жизнь:

this.actions$
    .ofType('FETCH')
    .map(a => a.payload)
    .switchMap(query => this.apiService.fetch$(query)
        .map(result => ({ type: 'SUCCESS', payload: result }))
        .catch(e => Observable.of({ type: 'FAIL', payload: e}))) // re-emit

Это справедливо для любого rxjs Observable btw, что особенно важно учитывать при трансляции в несколько наблюдателей (например, ngrx store делает внутреннее использование внутреннего Subject).

Ответ 3

Я пользуюсь более поздней версией ngrx (7.4.0), поэтому предложение картриджа:

.do((action) => console.log('Received ${action.type}'))

должно быть...

... = this.actions.pipe(
   tap((action) => console.log('Received ${action.type}')),
   ...

И в конце концов я обнаружил, что пропустил добавление моего нового экспорта эффектов в модуль, например:

EffectsModule.forRoot([AuthEffects, ViewEffects]),  // was missing the ', ViewEffects'

Ответ 4

Другая возможная причина заключается в том, что если вы использовали ng generate для создания модуля, в который импортировали эффекты, убедитесь, что он импортирован в модуль приложения, поскольку следующая команда "ng generate module myModule" не добавит его в модуль приложения.