Search code examples
angularrxjsngrx

Angular NgRx's Effect creates an infinite loop


first of all it is worth mentioning the fact that I still haven't very clear rxjs's operators. I studied them but in the practical act if I either use switchMap, mergeMap or map the result still seems the same.

The code below creates an infinite loop, I tried to place debuggers strategically in order to understand the flow but it must be strictly related to the HTTP response time, hence appears to jump between the effects in a very random way.

@Effect()
addNewDocument = this.actions$.pipe(
    ofType(UserActions.ADD_DOCUMENT),
    map((action: UserActions.AddNewDocument) => {
        return action.document;
    }),
    switchMap((document: FormData) => {
        return this.store.select('user').pipe(
            map((user) => {
                return {
                    document,
                    uuid: user.uuid
                };
            })
        );
    }),
    switchMap((payload: { document: FormData, uuid: string }) => {
        return this.httpClient.post(
            `${environment.apiUrl}user/${payload.uuid}/documents`,
            payload.document
        ).pipe(
            mergeMap((response: any) => {
                return [
                    {
                        type: UserActions.ADDED_DOCUMENT,
                        response
                    }
                ];
            }),
            catchError((error) => of(new AppActions.GenericError(error)))
        );
    })
);

@Effect({dispatch: false})
addedNewDocument = this.actions$.pipe(
    ofType(UserActions.ADDED_DOCUMENT),
    tap(() => {
        this.router.navigate(['/user/documents']);
    })
);

My intent is:

  1. Intercept UserActions.ADD_DOCUMENT action
  2. keep track of the uuid for the http request
  3. Make the request and handle the response through the event UserActions.ADDED_DOCUMENT in order to update the state through its reducer

I'd really appreciate if you help me to understand what's wrong. If you think I missused some operators or you know a better method to use them, please let me know.

Thank you in advance.


Solution

  • It appears that your user state is changing in your application which is causing the looping in the effect. Try using take(1) while taking user state in effect like this:

    switchMap((document: FormData) => {
            return this.store.select('user').pipe(
                take(1),
                map((user) => {
                    return {
                        document,
                        uuid: user.uuid
                    };
                })
            );
        })
    

    take(1) will complete the observable and will tear down the action until the next UserActions.ADD_DOCUMENT action dispatched. Hope it helps.