This episode covers state management and reactive patterns: state management patterns with RxJS, BehaviorSubject and ReplaySubject as state services, an introduction to NgRx or Akita, and how to manage application side effects in a structured way.

The bigger the application, the more state there is to manage: logged-in user, cart, filters, search results. Putting all state in components makes the code hard to predict and hard to test.
Episode 10 covers state management with RxJS patterns, BehaviorSubject and ReplaySubject as state services, an introduction to libraries like NgRx or Akita, and structured side effect management. You'll see patterns that connect reactive thinking with real application needs.
Good state management follows a one-way flow: components send actions to a service, the service updates the state, and the new state flows back to components through Observables. No component directly modifies another component's state.
The simplest pattern is a single service that owns the state and exposes a stream:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { Todo } from './todo.model';
@Injectable({ providedIn: 'root' })
export class TodoService {
private readonly state = new BehaviorSubject<Todo[]>([]);
readonly todos$ = this.state.asObservable();
setTodos(data: Todo[]): void {
this.state.next(data);
}
tambah(todo: Todo): void {
this.state.next([...this.state.getValue(), todo]);
}
}BehaviorSubject holds the current value and sends it to new subscribers. todos$ is the public stream components can subscribe to, while state stays private. All state changes go through service methods — that's the only path.
import { ReplaySubject } from 'rxjs';
const notif$ = new ReplaySubject<string>(3);
notif$.next('Perubahan 1');
notif$.next('Perubahan 2');
notif$.subscribe((msg) => console.log(msg));new ReplaySubject<string>(3) stores the three most recent messages. When subscribe happens after two next calls, both messages are received immediately. This pattern is useful for small caches or events you can afford to miss.
Components receive state through todos$ and render it with the async pipe, which manages subscriptions automatically:
<li *ngFor="let todo of todos$ | async; trackBy: trackById">
{{ todo.judul }}
</li>todos$ | async subscribes to the stream and cleans it up when the component is destroyed — no manual ngOnDestroy and no risk of memory leaks.
NgRx brings the Redux pattern to Angular: a single state, an action for every change, and pure reducers that compute the new state.
import { createAction, createReducer, on } from '@ngrx/store';
export const tambahTodo = createAction('[Todo] Tambah', (judul: string) => ({ judul }));
const initialState: string[] = [];
export const todoReducer = createReducer(
initialState,
on(tambahTodo, (state, { judul }) => [...state, judul]),
);createAction defines an action, createReducer shapes a reducer. NgRx's benefits: state can be debugged with DevTools, a single global source of truth, and consistent patterns for large teams. The cost: a lot of boilerplate — consider it only for state that's truly global and complex.
Akita is a lighter state management library that takes a stores-and-queries approach and uses a more OOP-like API. For most applications, a combination of services with BehaviorSubject (or signals) is enough; an external library is only worth adding when the complexity truly demands it.
Side effects — HTTP requests, localStorage writes, logging — shouldn't be scattered across components. A clean pattern: the state service only owns state, a data service handles HTTP, and components just wire them together.
export class TodoService {
private readonly state = new BehaviorSubject<Todo[]>([]);
readonly todos$ = this.state.asObservable();
muat(): void {
this.http.get<Todo[]>(url).subscribe({
next: (data) => this.state.next(data),
error: (err) => console.error('Gagal memuat todo', err),
});
}
}The muat method triggers the HTTP request, then the result enters state via state.next. The component just calls muat() and reads todos$. Side effects stay collected in the service, so components remain lean and easy to test.
Key takeaways:
BehaviorSubject holds the current value; ReplaySubject holds a number of recent values.async pipe manages stream subscriptions automatically.In the next episode, episode 11, we'll cover configuration and environment — using environment files and build configurations, setting up Angular CLI configuration and build targets, managing feature flags and API endpoints, and handling secrets and production settings securely.