← Back to Chapters

NgRx Effects

⚡ NgRx Effects

? Quick Overview

NgRx Effects are used to handle side effects in your Angular application, such as making API calls, persisting data, logging, navigation, or analytics. They listen for specific actions, perform some (often asynchronous) work, and then dispatch new actions with the results.

This keeps your reducers pure and focused only on updating state, while effects take care of talking to the outside world.

? Key Concepts

  • Effect: An observable that listens to actions and can dispatch new actions.
  • Actions: Events such as login, loginSuccess, loginFailure that describe what happened.
  • createEffect: Helper function to define an effect.
  • Actions stream: Injectable stream of all dispatched actions.
  • ofType: Filters the actions stream by action type.
  • RxJS operators: mergeMap, switchMap, concatMap, catchError are used to handle async workflows.

? Syntax & Theory

The typical structure of an NgRx effect looks like this:

? View Effect Pattern
// Generic pattern for an NgRx effect
effectName$ = createEffect(() =>
this.actions$.pipe(
ofType(someAction),
mergeMap(action =>
this.someService.doSomething(action.payload).pipe(
map(result => someActionSuccess({ result })),
catchError(error => of(someActionFailure({ error })))
)
)
)
);

Important points about this pattern:

  • createEffect wraps your observable and tells NgRx it is an effect.
  • this.actions$ is a stream of every action dispatched in the app.
  • ofType(someAction) filters only the actions you care about.
  • Inside the effect you use services to call APIs or perform side effects.
  • The effect usually returns new actions (success or failure) that reducers can handle.

? Basic Login Effect Example

? View Code Example
// auth.effects.ts - effect that handles the login API call
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { AuthService } from '../auth.service';
import { login, loginSuccess, loginFailure } from './auth.actions';
import { map, mergeMap, catchError } from 'rxjs/operators';
import { of } from 'rxjs';

@Injectable()
export class AuthEffects {
constructor(private actions$: Actions, private authService: AuthService) {}

login$ = createEffect(() =>
this.actions$.pipe(
ofType(login),
mergeMap(action =>
this.authService.login(action.credentials).pipe(
map(user => loginSuccess({ user })),
catchError(error => of(loginFailure({ error })))
)
)
)
);
}

? How This Login Effect Works

  • The login action is dispatched from a component with user credentials.
  • The login$ effect listens for login using ofType(login).
  • It calls AuthService.login() to perform the actual HTTP request.
  • On success, it maps the result to a loginSuccess action containing the user data.
  • On failure, it catches the error and returns a loginFailure action.

? Live Flow / Explanation

  1. User submits the login form in the UI.
  2. The component dispatches login({ credentials }).
  3. login$ effect receives the action from the Actions stream.
  4. The effect calls authService.login(credentials) (HTTP request to backend).
  5. If successful, loginSuccess({ user }) is dispatched and reducers update the auth state.
  6. If there is an error, loginFailure({ error }) is dispatched and you can show an error message.

Notice that reducers never perform the HTTP call. They simply react to loginSuccess and loginFailure actions to update state.

? Common Use Cases for Effects

  • Authentication flows (login, logout, refresh token).
  • Loading data from REST APIs or Firebase.
  • Saving data (create / update / delete) to a backend.
  • Showing notifications or snackbars in response to actions.
  • Routing/navigation side effects after certain actions (e.g., redirect on login).

? Tips & Best Practices

  • Use mergeMap for concurrent requests, switchMap to cancel previous requests, and concatMap to queue them.
  • Keep each effect focused on one responsibility.
  • Move complex business logic into services and call them from the effect.
  • Always handle errors using catchError and dispatch a clear failure action.
  • Name effects with a $ suffix by convention (e.g., login$).

? Try It Yourself

  • Create a login$ effect (like above) that calls an API and dispatches loginSuccess/loginFailure actions.
  • Add an initApp$ effect that loads initial data when an AppInit action is dispatched.
  • Experiment with mergeMap, switchMap, and concatMap in an effect that loads a list of items. Observe how they behave when multiple load actions are dispatched quickly.
  • Create an effect that navigates to a dashboard route after loginSuccess using Angular Router.