DEV Community

loading...

NGRX Selector & async confusion

chasestorydev profile image chasestory ・2 min read

Angular & NGRX Gotchas


We have documented the trials and errors of previous developers so that you can avoid making the same mistakes and save time when building new features.

Side Note: this information is also based upon research, and not being able to locate within documentation, or Stackoverflow questions about how to do this in a clear, and concise way...

Selectors


When creating selectors with NGRX, it is presumed that when trying to access that variable data, you will see specifically that data that you defined in the associated selector object.

this.caseFolder$ = this.store.pipe(
  select(fromCaseSummary.getCurrentCaseFolder)
);
Enter fullscreen mode Exit fullscreen mode

This didn't seem to work for me.

When trying to visualize the data, or pass a specific attribute to a dispatch action from within a component, I have found that you must access that variable with a subscribe method provided by RXJS (This assumes you are familiar with this angular library/observable pattern).

Like so:

this.store.pipe(take(1)).subscribe(d => this.user = d);

There are a couple different ways to handle subscribing, and unsubscribing (to prevent memory leaks, so be sure to do so!).

Making API calls dependent on a response from a previous API call


Making an API call that is dependent upon a response from a previous API with NGRX effects is not as straight forward as calling it from the component right after the defined dispatch call of the specified action.

23:this.store.dispatch(fromLoginActions.sessionRefresh({user: this.user, tokenInputText: this.tokenInput}));

24:this.store.dispatch(fromCaseActions.getSummaryDetails({caseFolderId: this.caseIdInput, user: this.user.userSession.user}));
Enter fullscreen mode Exit fullscreen mode

The reason for this is NGRX effects are Asynchronous by design. Meaning that the next line that you defined (24) would be triggered immediately after, not once the previous call is completed.

In order for us to properly do this, it seems we must call the subsequent action within the effects method for the specified action, within the response map, and using the corresponding concatMap, mergeMap, SwitchMap methods provided by RXJS.

Like so:

switchMap( (response: any) => [
 CaseSummaryActions.loadCaseSummarySuccess(response),
  patientActions.getPatient({                       
   patientID:response.data.attributes.patient_id,user:action.user
  }),
  groupPlanActions.getGroupPlans({
   groupID: response.data.attributes.group_id, user: action.user
  })
]),
catchError( (error: any) =>                                         
  of(CaseSummaryActions.loadCaseSummaryFailure(error))),
)
Enter fullscreen mode Exit fullscreen mode

*If you have any additional insight, or can point me into the direction for more definitive answers, I will be grateful! *

This may not be the right way, but it got me unblocked in order to make my deliverable...

Discussion (1)

Collapse
bttb profile image
bttb

You shouldn't use switchMap in the last example. Better is concatMap. Otherwise a 2nd action would cancel the 1st request, if it occured before the server responded. This might be a problem, if you have a loading indicator which is based on a number of active requests.

Forem Open with the Forem app