๐Ÿณ IntuitiveFE
Login
โ† All concepts

Angular NgRx

โฑ๏ธ ~18-minute bite ยท solve the sandbox to master

0%lesson
๐Ÿง’

5-Year-Old Metaphor

โ€” The physical, real-world picture. No jargon.

NgRx = a post office.Actions = addressed envelopes โ€” you drop them in the outbox, you don't deliver them yourself.

Reducers = the sorting room โ€” reads the address (action type), updates the right PO box (state slice). Pure, deterministic, testable. Effects = the international courier โ€” handles deliveries that require going outside (HTTP, localStorage, WebSocket). Dispatches a new envelope when done. Store = the PO box grid โ€” single source of truth. Selectors = your key โ€” only opens your box, and caches the result until the contents change.

๐ŸŽ›๏ธ

Interactive Sandbox

โ€” Move something, see it react instantly.

Current State

{
count: 0,
doubled: 0
}

Reducer Case

waiting for action...

on() handler that matched

Action Log

No actions yet

provideStore()

Modern NgRx uses createFeature() and provideStore() in ApplicationConfig. The old StoreModule.forRoot() still works but is the NgModule path.

js
1// โœ… Modern standalone setup (NgRx 15+)
2// app.config.ts
3import { ApplicationConfig } from '@angular/core';
4import { provideStore } from '@ngrx/store';
5import { provideEffects } from '@ngrx/effects';
6import { provideStoreDevtools } from '@ngrx/store-devtools';
7import { counterFeature } from './counter/counter.feature';
8import { CounterEffects } from './counter/counter.effects';
9ย 
10export const appConfig: ApplicationConfig = {
11 providers: [
12 provideStore(), // root store (empty)
13 provideState(counterFeature), // register feature slice
14 provideEffects(CounterEffects), // register effects class
15 provideStoreDevtools({
16 maxAge: 25,
17 logOnly: !isDevMode(),
18 }),
19 ],
20};
21ย 
22// โŒ Old NgModule setup (still valid, not recommended for new code)
23@NgModule({
24 imports: [
25 StoreModule.forRoot({ counter: counterReducer }),
26 EffectsModule.forRoot([CounterEffects]),
27 StoreDevtoolsModule.instrument({ maxAge: 25 }),
28 ],
29})
30export class AppModule {}
31ย 
32// createFeature() auto-generates selectors from the state shape
33export const counterFeature = createFeature({
34 name: 'counter',
35 reducer: counterReducer,
36});
37// Generates: counterFeature.selectCounterState, counterFeature.selectCount, etc.
  • โ†’provideState() registers a feature slice in standalone โ€” equivalent to StoreModule.forFeature()
  • โ†’createFeature() auto-derives selectors for every top-level state property
  • โ†’StoreDevtools connects to Redux DevTools browser extension โ€” essential for debugging
1/5 explored
๐ŸŽฏ

Challenge

Explore all 5 NgRx patterns. Use the action dispatcher to watch the state machine in action.

Try it
๐ŸŽฏ

Why Should I Care?

โ€” The exact interview question + the bug it kills.

Q: When is NgRx overkill?

Small apps, local UI state (open/close, loading flags, selected item). Use component state or signals/services instead. NgRx adds ~10โ€“15KB and meaningful boilerplate. Reach for it when multiple disconnected components share state that changes from multiple sources.

Q: What's NgRx Signals Store?

@ngrx/signals is a lightweight signal-based store without RxJS effects boilerplate. Define state with signalStore(), add methods directly, use withComputed() for derived values. No actions/reducers/effects ceremony โ€” closer to Zustand or Pinia.

Q: How does createSelector memoization work?

It checks each input selector's output by reference equality. If none changed, it returns the cached projector result without re-running. One cache entry per selector instance โ€” selector factories (functions returning selectors) avoid cache collisions when called with different arguments.

๐Ÿ”ฌ

The Deep Dive

โ€” Spec refs, engine internals, the minutiae.

NgRx internals, ecosystem packages, and the signals-based future:

  • 01

    createFeature() auto-generates one selectX selector for each top-level state property. Use extraSelectors to add derived ones in the same file.

  • 02

    Memoization internals: NgRx stores the last [inputs, result] pair. On each select(), it runs input selectors, compares outputs with the cached inputs using ===, and returns the cached result if unchanged.

  • 03

    NgRx Entity (@ngrx/entity): EntityAdapter manages normalized collections. selectAll(), selectEntities(), selectTotal() out of the box. Handles add/update/remove/upsert without writing spread logic.

  • 04

    @ngrx/component-store: local state scoped to a component tree. No global store โ€” great for feature-level state that doesn't need to survive navigation.

  • 05

    NgRx Signals Store API: signalStore({ withState({ count: 0 }), withComputed(({ count }) => ({ doubled: computed(() => count() * 2) })), withMethods(...) })

๐ŸŽค

Interview Questions

โ€” Real questions from real interviews โ€” with answers.

NgRx for complex shared state across disconnected features; use signals/services for co-located or simple state.

'[Source] Event' format groups actions by source in DevTools and prevents collisions across features.

Impure reducers break Redux DevTools time-travel and make state unpredictable and untestable.

It caches one [inputs, result] pair and recomputes only when any input selector's output changes by reference.

dispatch: false for side-effect-only effects. Forgetting causes the effect's emissions to be dispatched as actions, often causing infinite loops.

@ngrx/signals is a lightweight signal-based store without actions/reducers/effects โ€” closer to Zustand than Redux.

๐ŸŽฎ

Memory Game

โ€” Quick quiz โ€” lock the concept in long-term memory.
1/4

What does provideEffects([CounterEffects]) register in a standalone Angular app?