0
votes

I have a dead simple ngrx structure which loads a JSON array from a REST endpoint and puts it into the state. It doesn't load the entire array at once, but by pages of 30 elements, and the result is appended to what was loaded previously. So it's a typical pagination. The action responsible for the entire operation is dispatched repeatedly, with a parameter specifying the page number to load. It calls an effect, which in turn calls a method of a service.

The problem is that the action doesn't run at all after the first call. At the first call it works perfectly. At the second, third, etc. attempt it seems to stop before the act() operator, but it doesn't throw any errors or do anything at all.

Here's the action:

export const LoadSiteList = createAction(
    ActionTypes.LoadSiteList,
    props<{ page: number }>()
);

The effect:

    loadSiteList = createEffect(() => this.actions.pipe(
        ofType(LoadSiteList),
        act({
            project: payload => {
                return this.sitesService.loadSiteList(payload['page']).pipe(
                    map((siteList: Site[]) => UpdateSiteList({ siteList })),
                )
            },
            error: () => { alert('Unable to load site list!'); return DoNothing() }

        })
    ));

The service method in sitesService:

  loadSiteList(page: number = 1): Observable<any> {
    return Observable.create(observer => {
      const temp = this.httpClient.get(`${environment.rootUrl}sites?_page=${page}`)
        .subscribe(
          data => observer.next(data),
          error => observer.error(error),
          () => temp.unsubscribe()
        );
    });
  }

And this is how I call it from my component:

this.store.dispatch(LoadSiteList({ page: this.page }));

I'm pretty sure it's not a missing import because it wouldn't work at all then. The unsubscribe() in the service method also isn't at fault. I tried to put a tap() operator between ofType() and map() in the effect, and it fires, so the action does get there. Any console output in the act() operator's callback branches will not appear, so that operator doesn't fire. But why?

Just in case here's the reducer method too. It's never gets called though.

    on(
        SitesActions.UpdateSiteList,
        (state, siteList) => ({
            ...state,
            sites: [...state.sites, ...siteList.siteList]
        })
    ),
1

1 Answers

0
votes

Nothing feels better than answering my own question while feeling a bit stupid. So it seems the bug was in the pipe structure of the effect. Here's the working version:

loadSiteList$ = createEffect(() => this.actions$.pipe(
   ofType(LoadSiteList),
   map(payload => payload['page']),
   mergeMap(page => this.sitesService.loadSiteList(page)
       .pipe(
           map((siteList: Site[]) => UpdateSiteList({ siteList })),
           catchError(error => { alert(`Error ${error.status} loading data.`); return EMPTY; })
       ))
));

Another lesson learned: ngrx has an EMPTY value when you don't want to dispatch another action from your effect. The more you know.