Search code examples
javascriptangularreduxrxjsngrx

Avoid multiple request when using ngrx effect


I would like to make two treatments on a same api call data.

I have a first effect:

loadSchedulings$ = createEffect(() =>
  this.actions$.pipe(
    ofType(ESchedulesActions.GetSchedulesByDate),
    mergeMap(() =>
      this.apiCallsService.getSchedulings().pipe(
        map(trips => ({ type: ESchedulesActions.GetSchedulesByDateSuccess, payload: trips })),
        catchError(() => EMPTY)
      )
    )
  )
);

I call getSchedulings service method which make an api call then a treatment 1 on data is done

ApiCallsService :

getSchedulings() {
  return this.http.get<ISchedules>(this.SchedulingByDateLocal2).pipe(
      ...
      return groupByDate;
    })
  );
}

I would like to make a second treatment on the same data source. (raw data got from api ) but in parallel of the first because they are independent

So by logic I create a second effect

loadDirections$ = createEffect(() =>
  this.actions$.pipe(
    ofType(ESchedulesActions.GetSchedulesByDate),
    mergeMap(() =>
      this.apiCallsService.getDirections().pipe(
        map(trips => ({ type: ESchedulesActions.GetDirectionsByDateSuccess, payload: directions})),
        catchError(() => EMPTY)
      )
    )
  )
);

Then in apiCallService I should have a method

getDirections() {
  return this.http.get<ISchedules>(this.SchedulingByDateLocal2).pipe(
      ...
      return groupByDirections;
    })
  );
}

The problem here is that I will have two requests for the same data.

To summarize the actual workflow :

LoadSchedulings ( effect ) ==> loadSchedulings ( service ) ==> API Call ==> treatment 1 LoadDirections ( effect ) ==> loadDirections ( service ) ==>(Same) API Call ==> treatment 2

So I would like to only use the first api request's data for two treatments

Update: According to the response of Manuel Panizzo I should have something like this ?

getRawData() {
  return this.http.get<ISchedules>(this.SchedulingByDateLocal2)
}

Effect.ts

loadSchedulings$ = createEffect(() =>
  this.actions$.pipe(
    ofType(ESchedulesActions.getRawData),
    pipe((data) =>
      this.apiCallsService.getSchedulings(data).pipe(
        map(trips => ({ type: ESchedulesActions.GetSchedulesByDateSuccess, payload: trips })),
        catchError(() => EMPTY)
      )
    ),
    pipe((data) =>
      this.apiCallsService.getDirections(data).pipe(
        map(directions=> ({ type: ESchedulesActions.GetDirectionsByDateSuccess, payload: directions})),
        catchError(() => EMPTY)
      )
    ),
  )
);

Solution

  • I think you could also dispatch a getRawDataSuccess action (that performs 1 api call)

    getRawData$ = createEffect(() =>
      this.actions$.pipe(
        ofType(ESchedulesActions.getRawData),
        mergeMap(() =>
          this.apiCallsService.getRawData().pipe(
            map(data => ({ type: ESchedulesActions.GetRawDataSuccess, payload: data })),
            catchError(err => ({ type: ESchedulesActions.GetRawDataError, payload: err }))
          )
        )
      )
    );
    

    Then create one effect per treatment listening for getRawDataSuccess action:

    getSchedulesByDate$ = createEffect(() =>
      this.actions$.pipe(
        ofType(ESchedulesActions.getRawDataSuccess),
        map((action) => {
          return {
            type: ESchedulesActions.GetSchedulesByDateSuccess,
            payload: action.payload.schedulesByDate,
          }
        })
      )
    );
    
    getDirectionsByDate$ = createEffect(() =>
      this.actions$.pipe(
        ofType(ESchedulesActions.getRawDataSuccess),
        map((action) => {
          return {
            type: ESchedulesActions.GetDirectionsByDateSuccess,
            payload: action.payload.directionsByDate,
          }
        })
      )
    );
    

    This would be cleaner IMO and will theoretically run in parallel too.