NgRx Effects
NgRx Effects handle side effects, like API calls, outside of reducers and components by listening to the stream of dispatched actions and, when a matching action appears, running async work and dispatching a new action with the result.
Why it exists
NgRx reducers are required to be pure functions: same state and action in, same new state out, with no API calls, no timers, no randomness. Real applications obviously need to call APIs, read local storage, and navigate in response to user actions, none of which is allowed inside a pure reducer. Effects exist to give that necessary impurity a dedicated, testable home, separate from both reducers and components, so the store's update logic stays predictable while side effects still happen somewhere principled.
The mental model
An Effect is a listener standing beside the store, not part of it. It never touches state directly. It watches the stream of every action dispatched across the whole app go by, and when one it cares about appears, it goes off and does something asynchronous, then comes back and dispatches a brand new action describing what happened. It is a translator between something that happened in the world and the resulting action for the store to handle.
How it works
An Effect is built with createEffect, wrapping the actions stream, an RxJS Observable of every action dispatched anywhere in the app, piped through ofType with the action it cares about to filter down to just that one. From there, a flattening operator like mergeMap, switchMap, or exhaustMap calls a service method that returns an Observable, typically an HTTP request, then map transforms a successful response into a success action and catchError turns a failure into a failure action. The Effects runtime automatically dispatches whatever action comes out the other end back into the store, so the whole cycle, trigger, side effect, result, stays entirely inside RxJS operators.
When it matters
It matters anywhere state changes need an API call, navigation, or persistence attached to them. The footgun is picking the wrong flattening operator: mergeMap on a search-as-you-type effect fires every keystroke's request in parallel with no cancellation, letting stale responses arrive after fresh ones. switchMap fixes that by cancelling the previous request, but using switchMap on a save action can wrongly cancel an in-progress save if triggered twice, where exhaustMap, which ignores new triggers until the current one finishes, is the correct choice.
A concrete example
An Effect listens for a loadUsers action, calls the users service with switchMap, and dispatches a loadUsersSuccess action with the response on success or a loadUsersFailure action with the error on failure. The component that needs the list only ever dispatches loadUsers and reads a users selector, never touching the HTTP client itself.
Interview question
An NgRx Effect for a save button uses switchMap around the API call. A user double-clicks save quickly. What goes wrong?
- a.switchMap cancels the first in-flight save request when the second click triggers a new one, so the first save may never actually completeCorrect
- b.switchMap has no effect on this scenario since it behaves identically to mergeMap for HTTP requests
- c.switchMap throws a runtime exception because two identical actions cannot be dispatched in quick succession
- d.switchMap queues the second save request until the first one fully completes, so both saves always succeed in order
Why? this is the answer
switchMap cancels whatever inner Observable is currently in flight the moment a new source value arrives, so a second save click cancels the first save request mid-flight, which is the opposite of what a save action needs. It does not queue safely like exhaustMap would, it behaves very differently from mergeMap, which would let both run concurrently, and NgRx does not throw for repeated actions.
Just read this? Test yourself on what you have been reading.
Read the original → ngrx.io
- #angular
- #ngrx
- #state management
- #rxjs
Put your scrolling time to good use
Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on angular — each one lists the topics its interview covers.
See open roles