Question presented to candidate:
"In our NgRx app, a developer put the HttpClient call for loading users directly in the component and dispatches the result, and another suggested calling the API inside the reducer. Explain what NgRx Effects are for, why side effects are kept out of reducers, and how you would write the load-users flow."
What a strong answer should cover:
- An effect is an Observable pipeline that listens to the action stream, performs side effects (HTTP, navigation, storage, timers), and usually maps the outcome to new actions that are dispatched back to the store.
- Reducers must be pure and synchronous: same input, same output, no I/O. That keeps them predictable, replayable (DevTools time travel), and trivially testable; an HTTP call cannot return the next state synchronously anyway.
- Components only dispatch events ("[Users Page] Opened") and read selectors; they do not know where data comes from.
- The canonical shape:
ofType(opened)then a flattening operator (switchMap,exhaustMap,concatMap,mergeMap, chosen deliberately), the API call,mapto a success action, andcatchErrorinside the inner pipe mapping to a failure action. - Variants: functional effects (
createEffect(fn, { functional: true })), non-dispatching effects ({ dispatch: false }), and the default error handler that resubscribes an effect after an uncaught error (up to 10 times). - Testing:
provideMockActionsand marble or plain subscription tests.
Clarifying questions expected:
- "What should happen if the user triggers the load again while one is in flight?"
- "Should a failure be shown to the user, retried, or both?"
Code / implementation expected: Yes β actions, a reducer handling success and failure, and an effect with the correct operator and error handling.