State Management in Angular - Textnotes

State Management in Angular


State management in Angular refers to managing data across components efficiently. Applications can use simple service-based state, RxJS, or advanced libraries like NgRx for predictable state management.

1. Service-based State Management

Use Angular services to share state between components.


@Injectable({ providedIn: 'root' })
export class CounterService {
count = 0;

increment() { this.count++; }
decrement() { this.count--; }
}

Inject the service in components:


constructor(private counterService: CounterService) {}

increment() { this.counterService.increment(); }
  1. Simple and easy for small apps
  2. State persists as long as the service is alive

2. RxJS-based State

Use Subjects or BehaviorSubjects to create reactive shared state.


private countSubject = new BehaviorSubject<number>(0);
count$ = this.countSubject.asObservable();

increment() {
this.countSubject.next(this.countSubject.value + 1);
}
  1. Components subscribe to count$ to get updates
  2. Makes state reactive and easier to manage asynchronously

3. LocalStorage & SessionStorage

For persistent state across reloads, store data in LocalStorage or SessionStorage:


localStorage.setItem('user', JSON.stringify(user));
const storedUser = JSON.parse(localStorage.getItem('user') || '{}');
  1. LocalStorage persists until manually cleared
  2. SessionStorage persists only for the current tab/session

4. NgRx Basics

NgRx is a Redux-inspired state management library for Angular. It provides a single source of truth, making large apps predictable.

4.1 Actions

Define events that describe state changes:


import { createAction, props } from '@ngrx/store';

export const increment = createAction('[Counter] Increment');
export const decrement = createAction('[Counter] Decrement');
export const set = createAction('[Counter] Set', props<{ value: number }>());

4.2 Reducers

Reducers define how the state changes in response to actions:


import { createReducer, on } from '@ngrx/store';
import { increment, decrement, set } from './counter.actions';

export const initialState = 0;

export const counterReducer = createReducer(
initialState,
on(increment, state => state + 1),
on(decrement, state => state - 1),
on(set, (state, { value }) => value)
);

4.3 Effects

Effects handle side effects, like API calls, outside of the reducer:


import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Injectable } from '@angular/core';
import { EMPTY } from 'rxjs';
import { map, mergeMap, catchError } from 'rxjs/operators';
import { ApiService } from '../api.service';
import * as CounterActions from './counter.actions';

@Injectable()
export class CounterEffects {
loadData$ = createEffect(() =>
this.actions$.pipe(
ofType(CounterActions.loadData),
mergeMap(() => this.api.getData()
.pipe(
map(data => CounterActions.loadDataSuccess({ data })),
catchError(() => EMPTY)
))
)
);

constructor(private actions$: Actions, private api: ApiService) {}
}

4.4 Store

The Store is a global state container for the app:


import { Store } from '@ngrx/store';
import { increment } from './counter.actions';

constructor(private store: Store<{ counter: number }>) {}

incrementCounter() {
this.store.dispatch(increment());
}

4.5 Selectors

Selectors retrieve data from the store:


import { createSelector } from '@ngrx/store';

export const selectCounter = (state: { counter: number }) => state.counter;

Usage in component:


counter$ = this.store.select(selectCounter);

Summary

  1. Service-based state is simple for small apps.
  2. RxJS-based state provides reactive updates between components.
  3. LocalStorage / SessionStorage allows persistent state across reloads.
  4. NgRx provides a structured, predictable state management solution for large-scale apps:
  5. Actions → define events
  6. Reducers → define state changes
  7. Effects → handle side effects
  8. Store → centralized state container
  9. Selectors → read state efficiently