NgRx Effects: Proper Setup, Error Handling and Common Pitfalls
How to configure NgRx Effects with proper error handling, operator selection, and module registration to avoid silent failures in Angular apps.
07 Jun 2026, 12:45 UTC

Problem: Effect silently fails to dispatch success actions
When an NgRx effect does not dispatch the expected success action, the component UI stays stale and the store appears inconsistent. The usual cause is a missing return statement or an error path that does not emit a failure action.
Useful takeaway
Always return the Observable from createEffect, register the effect with EffectsModule.forFeature in the feature module, and use catchError to wrap errors in a dispatched loadFailure action.
Mechanism: how createEffect works
createEffect returns an Observable that subscribes to the store's action stream (this.actions$). When a matching action flows through, the pipe chain runs step by step:
ofType(load)filters the action stream, letting only actions of the specified type pass.mergeMapswitches to an asynchronous source, such as an HTTP request, while allowing concurrent emissions.maptransforms the HTTP response payload into a success action, for exampleloadSuccess({ payload: data }).catchErrorintercepts any error from the upstream observable and repurposes it withof(loadFailure({ payload: error })), ensuring the stream never completes unexpectedly.
import { createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { mergeMap, map, catchError } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';
import * as AuthActions from './auth.actions';
@Injectable({ providedIn: 'root' })
export class AuthEffects {
load$ = createEffect(() => this.actions$.pipe(
ofType(AuthActions.load),
mergeMap(() => this.http.get('/api/auth').pipe(
map(data => AuthActions.loadSuccess({ payload: data })),
catchError(error => of(AuthActions.loadFailure({ payload: error })))
))
));
}
Common mistakes and limits
- Missing return: The function passed to
createEffectmust return the Observable. Omitting the return value or returning a plain object breaks the stream and causes silent failures. - Operator selection:
switchMapcancels the previous HTTP request when a new action arrives, which is ideal for login flows but can cause unexpected cancellations if navigation occurs mid-request.mergeMapfires concurrent requests; useconcatMapto queue them sequentially. - Effect registration: Effects must be provided via
EffectsModule.forRoot([AuthEffects])at the app root orEffectsModule.forFeature([AuthEffects])in a feature module. Forgetting to register means the effect never instantiates. - Error shape: The error object from
catchErroris the raw HTTP error; structure it consistently (e.g.,loadFailure({ payload: error.message })) so consumers handle it predictably.
Practical verification
- Open the NgRx Store DevTools in the browser. Trigger the
loadaction and verify thatloadSuccessorloadFailureappear in the action timeline. - Write a unit test using
provideMockStoreand Jasmine marbles: dispatchload, expect the stream to produceloadSuccessorloadFailurebased on the mocked HTTP response.
When the effect is correctly configured, the store updates predictably and the UI reflects the latest data without manual subscription management.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.