web-dev-qa-db-fra.com

L'effet ngrx n'est pas appelé lorsque l'action est envoyée à partir du composant

J'ai un problème avec le magasin ngrx qui ne diffuse pas une action dans le sens censé y faire face.

Voici le composant qui essaie de distribuer:

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

Les actions:

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;

L'effet:

  @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);
        })
    );

Je peux voir dans le débogage que le composant appelle la méthode de répartition. Je peux également confirmer que StandardSigninAction est en effet instancié car le point d'arrêt dans le constructeur est atteint.

Mais le standardSignin$ l'effet n'est pas appelé ...

Qu'est-ce qui peut éventuellement provoquer un effet qui n'est pas appelé?

Comment puis-je déboguer ce qui se passe dans le magasin?

Puis-je avoir une aide s'il vous plait?

P.S. J'exécute l'effet ci-dessus comme suit dans mes importations:

EffectsModule.run(SessionEffects),

edit: Voici ma méthode SessionSigninService.signin (retourne un Observable)

  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'));
  }
14
balteo

Ce ne sera pas une réponse définitive, mais, espérons-le, ce sera utile.

Avant de commencer:

  • Assurez-vous que vous utilisez les dernières versions de @ngrx packages (qui conviennent à la version de Angular que vous utilisez).
  • Si vous avez mis à jour des packages, assurez-vous de redémarrer votre environnement de développement (c'est-à-dire redémarrer le bundler, le serveur, etc.)

Si vous ne l'avez pas déjà fait, vous devriez jeter un œil à l'implémentation de Store - afin de faire des suppositions éclairées sur ce qui pourrait mal se passer. Notez que le Store est assez léger. C'est à la fois un observable (utilisant l'état comme source) et un observateur (qui s'en remet au répartiteur).

Si vous regardez store.dispatch vous verrez que c'est un alias pour store.next , qui appelle next sur Dispatcher .

Appelant ainsi:

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

devrait juste voir une action émise par le répartiteur.

L'observable Actions qui est injecté dans vos effets est également assez léger. C'est juste un observable qui utilise le Dispatcher comme source.

Pour regarder les actions qui traversent l'effet, vous pouvez remplacer ceci:

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

avec ça:

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

ofType n'est pas un opérateur; c'est une méthode, donc pour ajouter une journalisation basée sur do, elle doit être remplacée par une filter.

Avec la journalisation en place, si vous recevez l'action, il y a quelque chose qui ne va pas dans la mise en œuvre de l'effet (ou peut-être que les chaînes/constantes des types d'action ne sont pas ce que vous pensez qu'elles sont et que quelque chose ne correspond pas).

Si l'effet ne reçoit pas l'action distribuée, l'explication la plus probable serait que le store par lequel vous répartissez le StandardSigninAction n'est pas le même store que votre effet est en utilisant - c'est-à-dire que vous avez un problème DI.

Si tel est le cas, vous devriez regarder ce qui diffère des autres SessionEffects qui, selon vous, fonctionnent. (Au moins, vous avez quelque chose qui fonctionne, ce qui est un bon endroit pour commencer à expérimenter.) Sont-ils envoyés à partir d'un module différent? Le module qui distribue StandardSigninAction est-il un module de fonctionnalité?

Que se passe-t-il si vous piratez l'un des SessionEffects pour remplacer son action distribuée par StandardSigninAction? L'effet s'exécute-t-il alors?

Notez que les questions à la fin de cette réponse ne sont pas des questions auxquelles je veux une réponse; ce sont des questions que vous devriez vous poser et enquêter.

16
cartant

Le flux de votre boutique peut s'arrêter en raison d'erreurs non gérées ou, peut-être plus confus, d'erreurs qui semblent "gérées" à l'aide de .catch qui tue le flux sans réémettre un nouvel Observable pour que les choses continuent.

Par exemple, cela tuera le flux:

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!

Mais cela gardera les choses en vie:

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

Cela est vrai pour tout rxjs Observable btw, ce qui est particulièrement important à prendre en compte lors de la diffusion à plusieurs observateurs (comme le fait ngrx store en interne en utilisant un Subject interne).

7
rob3c

J'utilise une version ultérieure de ngrx (7.4.0), donc la suggestion de cartan de:

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

devrait être...

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

Et à la fin, j'ai découvert que j'avais manqué d'ajouter ma nouvelle exportation d'effets au module, comme:

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