Search code examples
angularfirebasegoogle-cloud-firestorengrxngrx-effects

Effect dispatched an invalid action: undefined


I'm having some troubles tracking down why this error below is happening. The effect is being called, and it's running through it's steps however it seems to be unable to dispatch the Success of Failure action.

core.js:6456 ERROR Error: Effect "ProfileEffects.checkProfile$" dispatched an invalid action: undefined
at reportInvalidActions (ngrx-effects.js:213)
at MapSubscriber.project (ngrx-effects.js:281)

Here is my checkProfile$ effect:

public checkProfile$ = createEffect(() => {
    return this.actions$.pipe(
        ofType(loadCheckProfile),
        switchMap(() => {
            return this.authService.checkAuth().pipe(
                map((user) => {
                    if (user !== null) {
                      this.firestore.collection('users').doc(user.uid)
                      .ref
                      .get().then((doc) => {
                          if(doc.exists){
                              const profile: ProfileData = {
                                  uid: doc.data()['id'],
                              };
                               return loadCheckProfileSuccess({ data: profile });
                          } else {
                              this.router.navigate(['/']);
                              return loadCheckProfileFailure({ error: 'Failed to fetch profile'});
                          }
                      });
                    } else {
                        return loadCheckProfileFailure({ error: 'No authenticated User'});
                    }
                }),
                catchError((e) => of(loadCheckProfileFailure({ error: `${e}` })))
            );
        })
    );
});

Solution

  • That's happening because you're using a promise (this.firestore.collection('users').doc(user.uid).ref.get().then(...)) within the observable without waiting for it to return the value.

    In similar cases, you need to create a new Observable from the promise and chain it with the main Obsrvable, and you can achieve that in your case like the following:

    public checkProfile$ = createEffect(() => {
        return this.actions$.pipe(
            ofType(loadCheckProfile),
            switchMap(() => {
                return this.authService.checkAuth().pipe(
                    mergeMap((user) => {
                        // <<<<<< mergeMap used here to merge the new Observable with the main one.
                        if (user !== null) {
                            // >>>>> `from` is used to create an Observable from promise
                            return from(this.firestore.collection('users').doc(user.uid).ref.get()).pipe(
                                map((doc) => {
                                    if (doc.exists) {
                                        const profile: ProfileData = {
                                            uid: doc.data()['id'],
                                        };
                                        return loadCheckProfileSuccess({ data: profile });
                                    } else {
                                        this.router.navigate(['/']);
                                        return loadCheckProfileFailure({
                                            error: 'Failed to fetch profile',
                                        });
                                    }
                                })
                            );
                        } else {
                            return of(loadCheckProfileFailure({ error: 'No authenticated User' }));
                        }
                    }),
                    catchError((e) => of(loadCheckProfileFailure({ error: `${e}` })))
                );
            })
        );
    });