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?
2. What does ofType do in an effect?
3. Why use switchMap in effects?
Flashcards
Question
What is an NgRx effect?
Click to reveal answer
Answer
Listens to actions, performs side effects, dispatches new actions.
Question
What does ofType do?
Click to reveal answer
Answer
Filters action stream to specific action types.
Question
Why use switchMap in effects?
Click to reveal answer
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