DEV Community

Piotr Sobuś
Piotr Sobuś

Posted on • Edited on

1

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!

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

Cloudinary image

Video API: manage, encode, and optimize for any device, channel or network condition. Deliver branded video experiences in minutes and get deep engagement insights.

Learn more

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay