Search code examples
angularrxjsngrx

Dispatch an action after a previous one succeded with NgRx


The situation is as follow :

I have to make a HTTP call to retrieve an ID. With this ID, I will create a second endpoint to make a second HTTP call. My first action is FirstEntity.searchRequest. The effect - after making the HTTP request -will map over its success and create the action FirstEntity.searchSuccess. Afterward, I want to proceed SecondEntity.searchRequest at this precise moment. I came up with two solutions but they dont satisfy me.

First solution :

[...]
switchMap(([_, endpointGenerator, { id}]) =>
                    this.httpClient
                        .get<ApiFirstEntitySearchSuccess>(`${this.apiBaseUrl}${endpointGenerator(id)}`, {
                            headers: new HttpHeaders(this.httpHeadersConfig),
                            params: {}
                        })
                        .pipe(
                            takeUntil(
                                this.actions$.pipe(
                                    ofType(ApiFirstEntityActions.searchRequest),
                                    skip(1)
                                )
                            ),
                            map((success) => ApiFirstEntityActions.searchSuccess({ success })),
                            catchError((failure) => of(ApiFirstEntityActions.searchFailure({ failure })))
                        )
                ),
                tap(() => ApiSecondEntityActions.searchRequest()),

[...]

This one will tap() after the switchMap(). It looks OK and it should work, except that it will also be proceed even if the first HTTP call fails and is catch by the catchError.

Second solution :

[...]
switchMap(([_, endpointGenerator, { id}]) =>
                    this.httpClient
                        .get<ApiFirstEntitySearchSuccess>(`${this.apiBaseUrl}${endpointGenerator(id)}`, {
                            headers: new HttpHeaders(this.httpHeadersConfig),
                            params: {}
                        })
                        .pipe(
                            takeUntil(
                                this.actions$.pipe(
                                    ofType(ApiFirstEntityActions.searchRequest),
                                    skip(1)
                                )
                            ),
                            mergeMap((success) => {
                                return [ApiFirstEntityActions.searchSuccess({ success }), ApiSecondEntityActions.searchRequest()];
                            }),
                            catchError((failure) => of(ApiFirstEntityActions.searchFailure({ failure })))
                        )
                ),

[...]

Here, I create second entity action only if the first one succeed by passing an array of actions. However both actions will proceed (I guess?) concurrently, so it may occur that the second action will try to grab the ID from the state before it is filled.

So what is the best solution here? I guess there is a third option where I correctly create the ApiSecondEntityActions.searchRequest() only if ApiFirstEntityActions.searchRequest() has succeeded and ApiFirstEntityActions.searchSuccess() has completed ?

thank you!


Solution

  • I think the best solution would be creating additional effect, which listens for ApiFirstEntityActions.searchSuccess action and in that effect, you will dispatch ApiSecondEntityActions.searchRequest action.