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
- Over-engineering — Use for complex state only
- Circular updates — State updates trigger more updates
- Immutability — Always create new objects
- 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
Have questions on your tech stack, ongoing projects, or need one-to-one training?