Skip to main content

State Management Basics

Level: Advanced

ℹ️ What You'll Learn
  • What is state
  • State with services
  • State patterns
  • NgRx introduction
  • When to use state management

Service-Based State

@Injectable({ providedIn: 'root' })
export class StudentStateService {
private students = new BehaviorSubject<any[]>([]);
students$ = this.students.asObservable();

private loading = new BehaviorSubject(false);
loading$ = this.loading.asObservable();

constructor(private http: HttpClient) {}

loadStudents() {
this.loading.next(true);
this.http.get<any[]>('/api/students')
.pipe(
tap(data => this.students.next(data)),
finalize(() => this.loading.next(false))
)
.subscribe();
}

addStudent(student: any) {
const current = this.students.value;
this.students.next([...current, student]);
}
}

// Component
export class StudentListComponent implements OnInit {
students$ = this.state.students$;
loading$ = this.state.loading$;

constructor(private state: StudentStateService) {}

ngOnInit() {
this.state.loadStudents();
}
}

BehaviorSubject = state holder.

Shared State

@Injectable({ providedIn: 'root' })
export class SMSStateService {
private selectedStudent = new BehaviorSubject<any | null>(null);
selectedStudent$ = this.selectedStudent.asObservable();

private students = new BehaviorSubject<any[]>([]);
students$ = this.students.asObservable();

selectStudent(student: any) {
this.selectedStudent.next(student);
}

getSelectedStudent() {
return this.selectedStudent.value;
}
}

// Component A
selectStudent(student: any) {
this.state.selectStudent(student);
}

// Component B
selected$ = this.state.selectedStudent$;

Components share state through service.

NgRx Intro

// Store definition
export interface AppState {
students: StudentState;
}

export interface StudentState {
items: any[];
loading: boolean;
error: string | null;
}

// Actions
export const loadStudents = createAction('[Student] Load Students');
export const loadStudentsSuccess = createAction(
'[Student] Load Success',
props<{ students: any[] }>()
);
export const loadStudentsError = createAction(
'[Student] Load Error',
props<{ error: string }>()
);

// Reducer
export const studentReducer = createReducer(
initialState,
on(loadStudents, state => ({ ...state, loading: true })),
on(loadStudentsSuccess, (state, { students }) => ({
...state,
items: students,
loading: false
})),
on(loadStudentsError, (state, { error }) => ({
...state,
error,
loading: false
}))
);

// Effect
@Injectable()
export class StudentEffects {
loadStudents$ = createEffect(() =>
this.actions$.pipe(
ofType(loadStudents),
switchMap(() =>
this.http.get<any[]>('/api/students').pipe(
map(students => loadStudentsSuccess({ students })),
catchError(error => of(loadStudentsError({ error: error.message })))
)
)
)
);

constructor(
private actions$: Actions,
private http: HttpClient
) {}
}

NgRx = complex state management.

When to Use State Management

✓ Multiple components need same data ✓ Complex state transitions ✓ Time-travel debugging needed ✗ Simple component state (use @Input/@Output) ✗ Single component (use component state)

SMS State Example

@Injectable({ providedIn: 'root' })
export class SMSStateService {
private students = new BehaviorSubject<any[]>([]);
students$ = this.students.asObservable();

private selectedStudent = new BehaviorSubject<any | null>(null);
selectedStudent$ = this.selectedStudent.asObservable();

private loading = new BehaviorSubject(false);
loading$ = this.loading.asObservable();

constructor(private http: HttpClient) {}

loadStudents() {
this.loading.next(true);
this.http.get<any[]>('/api/students').subscribe({
next: data => {
this.students.next(data);
this.loading.next(false);
},
error: () => this.loading.next(false)
});
}

selectStudent(id: number) {
const student = this.students.value.find(s => s.id === id);
this.selectedStudent.next(student || null);
}

deleteStudent(id: number) {
const current = this.students.value;
this.students.next(current.filter(s => s.id !== id));
}
}

Key Takeaways

  • State = centralized data
  • BehaviorSubject = state holder
  • Services manage state
  • Observables expose state
  • NgRx = complex state management
  • Use when multiple components share state
💡 Backend Developer Tip

State management similar to shared database state in .NET. Centralized, single source of truth.

⚠️ State Issues
  1. Over-engineering — Use for complex state only
  2. Circular updates — State updates trigger more updates
  3. Immutability — Always create new objects
  4. Performance — Too many state changes = performance issues
🤖Use AI to Learn Faster

Use ChatGPT, Claude, or Copilot to go deeper on State Management. Try these prompts:

  • "When should you use state management?"
  • "What's difference between BehaviorSubject and Subject?"
  • "Why use NgRx instead of services?"
  • "Quiz me on state management"

💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.

nexcoding.in