Search code examples
angularrxjsngrx

NgRx Effect - type errors when getting a value for the store then piping into service call


I am trying to construct an NgRx effect and I just cannot see what is wrong in this example (and really want to understand)

I have the following

 public loadData = createEffect(() =>
    this.actions$
        .pipe(
            ofType(myActions.loadData),
            exhaustMap(_ =>
                this.store$.pipe(
                    tap(_ => { this.logger.info(`loadData`)}),
                    select(fromApp.getName),
                    first(),
                    map(name => {
                        from(this.getData(name))
                            .pipe(
                                 map(data => myActions.loadDataSuccess(data)))
                            
                    }),
                    catchError(error => {
                     tap(_ => this.logger.error(`loadData: ${error}`));
                     return of(new myActions.loadDataError(error));
                   })
     ))));


/** Service call **/
private async getData(name: string): Promise<DataState[]> {
     ...
}

This is how I interpret (obviously wrong somewhere) the flow..

Inside first exhaustMap, I use the this.store$.pipe to get an argument I need for my service call. So this the cal tofirst should return a string (name).

I then use map to "map" this string to the from(this.getData(name)). As my getData returns a Promise, I use from to convert to an observable.

I then pipe this observable result (an array of data) into another map, where I want to return the effects result, ie an action loadDataSuccess containing the server results.

The catchError wants to catch any errors from the map with the service method call inside of it.

The IDE shows me the following errors

    Type 'Observable<unknown>' is not assignable to type 'EffectResult<Action>'.
       Type 'Observable<unknown>' is not assignable to type 'Observable<Action>'.
         Property 'type' is missing in type '{}' but required in type 'Action'.ts(2322)

I don't know where I have gone wrong (I can't get it right even removing the catchError)?

Update 1

Following @Alif50 suggestion to use a switchMap, and also IDE told me to add a return, and removing the catchError. I now have..

public loadData  = createEffect(() =>
this.actions$
  .pipe(
    ofType(myActions.loadData ),
    exhaustMap(_ =>
      this.store$.pipe(
        tap(_ => { this.logger.info(`loadData`)}),
        select(fromApp.getName),
        first(),
        switchMap(name => {
          return from(this.getData(name))
            .pipe(map(data => myActions.loadDataSuccess(data)));
        }))
     )));

which gives no errors, so this is s a step forward, But I am still unable to get the catchError to work anywhere. So now I just need to know where to add the catchError?


Solution

  • I think the map should be a switchMap since we are switching to a new Observable.:

    map(name => { // change this map to a switchMap
     from(this.getData(name))
              .pipe(
                  map(data => myActions.loadDataSuccess(data)))
               }),
    

    !!!! Edit !!!!

    public loadData  = createEffect(() =>
    this.actions$
      .pipe(
        ofType(myActions.loadData ),
        exhaustMap(_ =>
          this.store$.pipe(
            tap(_ => { this.logger.info(`loadData`)}),
            select(fromApp.getName),
            first(),
            switchMap(name => {
              return from(this.getData(name))
                .pipe(
                   map(data => myActions.loadDataSuccess(data)),
                   // add the catchError here because I think this.getData is the point of failure
                   catchError(error => {
                     // You also can't have a tap there like you do
                     // in your catchError
                     // I would prefer this instead of the tap though
                     // this.logger.error(`loadData: ${error}`);
                     return of(new myActions.loadDataError(error)).pipe(
                        // use tap like this !!
                        tap(_ => this.logger.error(`loadData: ${error}`))
                     );
                   }),
               );
            }))
         )));