TobiDevloft
TobiDevloft

Reputation: 301

Effect is stuck in an infinite loop - Ngrx/effects

When new SaveModelAction() is called, the corresponding Effect gets stuck in an infinite loop.

This the effect:

@Effect() saveModelAction = this.action$.pipe(
    ofType(SAVE_MODEL),
    switchMap((action: any) => {
      const storageMetaData: StorageData = action.payload;
      return this.modelService.loadState()
        .pipe(
          switchMap((state: State) => {
            const model: Model = AnalysisUtils.convertStateToModel(state, storageMetaData);
            return this.modelService.saveModel(model)
              .pipe(
                map(() => new SavingModelCompleteAction),
                catchError((error: Error) => this.createErrorObservableAndLog(error))
              );
          }),
          catchError((error: Error) => this.createErrorObservableAndLog(error))
        );
    })
  );

Additional info:

  1. Within the relevant component, the SaveModelAction is only disptached once.
  2. When subscribing to the state at this.modelService.loadState(), take(1) is used.
  3. this.modelService.saveModel(model) does nothing except sending the model to the backend (and returning an observable.

Can anyone point me in the right direction on where my issue is? Thanks!

Upvotes: 1

Views: 2994

Answers (2)

Claudiu Lazăr
Claudiu Lazăr

Reputation: 43

I had the same issue, effect was stuck in an infinite loop and take(1) solved it for dispatching it only once.

The problem was that I had 2 actions which had the same type :(

This should work perfectly, so in case of infinite loop of effect or action, check the constants type :)

Good luck everyone!

Upvotes: 1

Márk
Márk

Reputation: 74

switchMap has a different purpose.

As I can see, you just want to flatten Observables, so you should use flatMap or mergeMap instead of switchMap. More about these operators: https://www.learnrxjs.io/operators/transformation/switchmap.html

Try something like this:

@Effect() saveModelAction = this.action$.pipe(
ofType(SAVE_MODEL),
mergeMap((action: any) => {
  const storageMetaData: StorageData = action.payload;
  return this.modelService.loadState()
    .pipe(
      mergeMap((state: State) => {
        const model: Model = AnalysisUtils.convertStateToModel(state, storageMetaData);
        return this.modelService.saveModel(model)
          .pipe(
            map(() => new SavingModelCompleteAction),
            catchError((error: Error) => this.createErrorObservableAndLog(error))
          );
      }),
      catchError((error: Error) => this.createErrorObservableAndLog(error))
    );
})
);

Also it's a common mistake that constants have the same primitive value. In your case please check that SAVE_MODEL_COMPLETE != SAVE_MODEL. If they have the same primitive value then you keep dispatching the same action.

Upvotes: 4

Related Questions