State Management (Services & NgRx)

In a large application, data must be shared across the entire framework reliably. Examples include tracking whether a user is currently logged in, containing a massive array of fetched dashboard data, or managing a shopping cart. This data is collectively called State.

Level 1: The Vanilla Service (BehaviorSubjects & Signals)

For 80% of applications, you do NOT need heavy external libraries! Angular's native Dependency Injection combined with RxJS BehaviorSubject or Angular signal() is more than enough.

import { Injectable, signal } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class CartService {
  
  // Using modern Signals for state!
  cartItems = signal<string[]>([]);
  
  addToCart(item: string) {
    this.cartItems.update(items => [...items, item]);
  }
  
  clearCart() {
    this.cartItems.set([]);
  }
}

Any component globally can inject CartService, read this.cart.cartItems(), and watch the UI update instantly.

Level 2: NgRx (Redux for Angular)

In massive enterprise architectures (like Banking Software with 500+ components), vanilla services can become a tangled, untraceable mess of updates.NgRx enforces a strict, predictable data flow pattern based on Redux.

  • Store: A massive, single immutable JSON tree containing your entire application's data.
  • Actions: Unique events dispatched to declare intent (e.g., [Cart] Add Item).
  • Reducers: Pure functions that listen for Actions, take the old State, and return a brand new State object safely.
  • Selectors: Smart queries to grab specific slices of the Store (e.g., grab just the 'user.id' without triggering updates on the whole user object).
  • Effects: Isolate API calls entirely away from UI components.
// A quick glimpse of an NgRx Action
export const addItem = createAction('[Cart Page] Add Item', props<{ item: string }>());

// Inside a Component, we strictly dispatch Actions. We NEVER mutate data directly.
this.store.dispatch(addItem({ item: 'Laptop' }));

NgRx SignalStore (The Future)

Recognizing that standard NgRx has painful amounts of boilerplate, the NgRx team recently released NgRx SignalStore. It integrates magically with Angular Signals, offering the immense scalability of Redux but with the sleek, lightweight syntax of Signals!

Conclusion

Only reach for NgRx if your app possesses vast, complex data interactions. Otherwise, standard Injectable services work beautifully. Quality code requires quality assurance, so let's look at Unit Testing next.