Skip to content
advanced Phase 12 · NgRx State Management

Effects for Side Effects

Move HTTP calls and other side effects out of components.

45m
0 problems
Topic Progress 0%

Creating Effects

Effects listen to dispatched actions and perform side effects like HTTP calls, then dispatch new actions with the results.

Creating an Effect

import { Injectable, inject } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { map, mergeMap, catchError } from 'rxjs/operators';
import * as UserActions from './user.actions';
import { UserService } from './user.service';

@Injectable()
export class UserEffects {
  private actions$ = inject(Actions);
  private userService = inject(UserService);

  loadUsers$ = createEffect(() =>
    this.actions$.pipe(
      ofType(UserActions.loadUsers),
      mergeMap(() =>
        this.userService.getUsers().pipe(
          map(users => UserActions.loadUsersSuccess({ users })),
          catchError(error =>
            of(UserActions.loadUsersFailure({ error: error.message }))
          )
        )
      )
    )
  );
}

Registering Effects

import { provideEffects } from '@ngrx/effects';
import { UserEffects } from './user.effects';

export const appConfig: ApplicationConfig = {
  providers: [
    provideStore({ users: userReducer }),
    provideEffects([UserEffects]),
  ],
};

Effect Flow

Component dispatches loadUsers()
  → Effect listens for loadUsers action
  → Effect calls userService.getUsers()
  → On success: dispatches loadUsersSuccess({ users })
  → On failure: dispatches loadUsersFailure({ error })
  → Reducer handles success/failure action
  → State updates
  → Selectors emit new values
  → Component re-renders

Effect Patterns

Effects support various patterns for different side-effect scenarios.

Non-Dispatching Effects

// Side effect without dispatching a new action
logActions$ = createEffect(() =>
  this.actions$.pipe(
    ofType(UserActions.addUser),
    tap(action => console.log('User added:', action.user))
  ),
  { dispatch: false } // Do not dispatch a new action
);

switchMap for Cancellation

searchUsers$ = createEffect(() =>
  this.actions$.pipe(
    ofType(UserActions.searchUsers),
    debounceTime(300),
    distinctUntilChanged(),
    switchMap(({ query }) =>
      this.userService.search(query).pipe(
        map(users => UserActions.searchUsersSuccess({ users })),
        catchError(error =>
          of(UserActions.searchUsersFailure({ error: error.message }))
        )
      )
    )
  )
);

switchMap cancels the previous inner Observable when a new action arrives, preventing stale responses.

concatMap for Sequential

// Process actions one at a time, in order
saveUsers$ = createEffect(() =>
  this.actions$.pipe(
    ofType(UserActions.saveUser),
    concatMap(({ user }) =>
      this.userService.save(user).pipe(
        map(saved => UserActions.saveUserSuccess({ user: saved })),
        catchError(error =>
          of(UserActions.saveUserFailure({ error: error.message }))
        )
      )
    )
  )
);

Multiple Actions in One Effect

loadUserPage$ = createEffect(() =>
  this.actions$.pipe(
    ofType(UserActions.loadUsers, UserActions.refreshUsers),
    mergeMap(() =>
      this.userService.getUsers().pipe(
        map(users => UserActions.loadUsersSuccess({ users })),
        catchError(error =>
          of(UserActions.loadUsersFailure({ error: error.message }))
        )
      )
    )
  )
);

Effect Error Handling

loadUsers$ = createEffect(() =>
  this.actions$.pipe(
    ofType(UserActions.loadUsers),
    mergeMap(() =>
      this.userService.getUsers().pipe(
        map(users => UserActions.loadUsersSuccess({ users })),
        catchError(error => {
          // Log error
          console.error('Failed to load users:', error);
          // Dispatch failure action
          return of(UserActions.loadUsersFailure({ error: error.message }));
        })
      )
    )
  )
);

Quiz

1. What is an NgRx effect?

Question 1 options

2. What does ofType do in an effect?

Question 2 options

3. Why use switchMap in effects?

Question 3 options

Flashcards

Question

What is an NgRx effect?

Answer

Listens to actions, performs side effects, dispatches new actions.

Question

What does ofType do?

Answer

Filters action stream to specific action types.

Question

Why use switchMap in effects?

Answer

Cancels previous inner Observable on new action. Prevents stale responses.

Revision Notes

Key Takeaways

  • 1. Effects handle side effects outside components
  • 2. ofType filters action streams
  • 3. switchMap cancels stale requests
  • 4. Effects dispatch new actions with results

Interview Tips

  • • Explain the effect flow
  • • Be ready to describe cancellation patterns
  • • Mention error handling in effects

Cheat Sheet

Effects Cheat Sheet

Create effect:

loadUsers$ = createEffect(() =>
  this.actions$.pipe(
    ofType(UserActions.loadUsers),
    mergeMap(() =>
      this.userService.getUsers().pipe(
        map(users => UserActions.loadUsersSuccess({ users })),
        catchError(error => of(UserActions.loadUsersFailure({ error: error.message })))
      )
    )
  )
);

Register:

provideEffects([UserEffects])

Patterns:

  • switchMap: cancel previous
  • concatMap: sequential
  • { dispatch: false }: no new action

Continue Learning