State management
State management with NgRx
Store, actions, reducers, effects and selectors — and when it is worth it.
NgRx is a Redux-style store for Angular: a single immutable state tree, actions describing events, pure reducers computing the next state, selectors deriving memoized slices, and effects handling async side effects as observable streams.
NgRx is a company ledger: nothing changes the books directly — you file a transaction (action), an accountant (reducer) posts it, and reports (selectors) read from the single source of truth.
Key concepts
1
The payoff is predictable, time-travel-debuggable state with a clear unidirectional flow — valuable for large apps with shared, cross-cutting state.
2
The cost is boilerplate and indirection; for local or simple state, a service with signals or a BehaviorSubject is often the better call. Newer NgRx SignalStore trims much of the ceremony.
NgRx SignalStoreBehaviorSubject
3
Pitfall: putting everything in the store, including component-local UI state, which bloats it. Interview angle: "when would you NOT use NgRx?" — small apps, mostly-local state, or where signals/services already suffice.
Pitfall:Interview angle:
typescript
export const load = createAction('[Users] Load');
export const loaded = createAction('[Users] Loaded', props<{ users: User[] }>());
export const usersReducer = createReducer(initialState,
on(loaded, (s, { users }) => ({ ...s, users, loading: false })));
export const loadUsers$ = createEffect(() => inject(Actions).pipe(
ofType(load),
switchMap(() => inject(Api).users().pipe(map(users => loaded({ users })))),
));