DEV Community

Piotr Sobuś
Piotr Sobuś

Posted on • Edited on

NGRX: Notify after action has been dispatched

While working with ngrx, sometimes we need to be notified after an action has been dispatched successfully in order to perform some kind of action (e.g: open or close a modal from a component).

One of the solution would be creating a Subject in the facade class and emit a value in the effect after the success action has been dispatched:

export class UserFacade {
  readonly userAdded$: Subject<boolean> = new Subject<boolean>();

  constructor(private readonly store: Store) {}

  addUser(user: UserPayload): void {
    this.store.dispatch(addUser({ user }));
  }
}
Enter fullscreen mode Exit fullscreen mode

Our effect would look like this:

@Injectable()
export class UserEffects {
  addUser$ = createEffect(() =>
    this.actions$.pipe(
      ofType(addUser),
      switchMap(({ payload }) =>
        this.userDataService.addUser(payload).pipe(
          map(() => addUserSucceeded()),
          tap(() => this.userFacade.userAdded$.next(true)),
          catchError((error) => of(addUserFailed(error)))
        )
      )
    )
  );

  constructor(
    private readonly actions$: Actions,
    private readonly userFacade: UserFacade,
    private readonly userDataService: UserDataService
  ) {}
}
Enter fullscreen mode Exit fullscreen mode

This solution is almost ideal. We don't need to directly subscribe to the effect and we can expose the information if action has been dispatched using facade pattern. On other hand, we have to create a Subject in the facade and control it in the effect. Instead, we can utilize an injectable service that provides an observable stream of all actions and listen on the success action:

export class UserFacade {
  readonly userAdded$: Observable<boolean> = this.actions$.pipe(
    ofType(addUserSucceeded),
    mapTo(true)
  );
}
Enter fullscreen mode Exit fullscreen mode

With this solution, we don't have to create additional class member and emit anything in the effect. Now, we can use it in our component:

@UntilDestroy()
@Component({ ... })
export class UserComponent {
  constructor(
    private readonly userFacade: UserFacade,
    private readonly matDialogRef: MatDialogRef<UserComponent>
  ) {}

  confirmClicked(): void {
    this.userFacade.addUser({ ... })

    this.userFacade.userAdded$
      .pipe(
        filter((added) => added),
        take(1),
        untilDestroyed(this)
      )
      .subscribe(() => this.matDialogRef.close());
  }
}
Enter fullscreen mode Exit fullscreen mode

If you have any questions, feel free to ask them in the comments!

Top comments (0)