Search code examples
angularngrxngrx-effects

NgRx Effects waiting for result


I have set up ngrx-effects in my project for fetching data:

dispatch in component -> effect calls http service -> http service returns data -> effect passes data to store through action -> component gets data from store observable

Now I am trying to implement simple "create" functionality with a similar approach, but I am struggling to see how to do this cleanly using effects.

As far as I can tell the order of operations would be:

dispatch "created" action in component -> reducer adds object to the store -> effect makes http request through service -> effect gets http result -> effect calls success/failure action

The issue is that there is no way to "wait" for the result of the http request. If the http request fails I would have to remove the object from the store again (which makes for a bad user experience).

Is there a clean way this kind of functionality is implemented with effects? Or is the answer just not to use effects for this and call the service directly from the component?


Solution

  • Common yet verbose way is to split the actions you use to save the object. One of them dispatches an action without saving the paylod to the store, it just triggers the effect. Effect runs and on success it dispatches another action to save the data into the store.

    export const myActions = createActionGroup({
      source: 'Some state',
      events: {
        Add: props<{ myData: MyDataDto }>(), // no reducer for this one, or just for setting `isLoading` flag
        'Add Success': props<{ result: MyDataDto }>(), // has reducer tied to it
        'Add Failure': emptyProps(),
    });
    // You can do the same thing even if you're not using createActionGroup(...)
    // Just write the actions on your own
    

    All actions from above can also change the state of your isLoading flag (if using any). No need to set it as a prop, Add sets it to true, both Success and Failure set it to false. Your effect can then look like this:

    add$ = createEffect(() => this.actions$.pipe(
      ofType(myActions.add),
      switchMap(({myData}) => /* make the HTTP request */),
      map(result => myActions.addSuccess({ result })),
      catchError(/* handle error */)
      );
    );
    

    Receive the payload in the action, use the service to save it to the DB and depending on the result, you either save it to the store, or handle an error.