RxJS
BehaviorSubject
Master RxJS BehaviorSubjects, covering state caching, state management patterns, and synchronous value access.
1. Learning Objectives
In this lesson, you will master the BehaviorSubject, the primary tool for reactive state management in Angular services. By the end of this topic, you will be able to:
- Explain the difference between a plain Subject and a BehaviorSubject.
- Initialize a BehaviorSubject with a default state value.
- Describe how BehaviorSubject handles late subscribers by emitting the current value immediately.
- Synchronously retrieve the current state using the
.getValue()method. - Build a reactive service that exposes readonly state while modifying it via private subjects.
2. Overview
A BehaviorSubject is a specialized type of Subject that represents a value that changes over time. It has two unique features: it requires an **initial value** upon creation, and it **caches the last emitted value**. When a new observer subscribes, it immediately receives the current cached value, even if they subscribed long after the value was emitted. It also allows synchronous reads of the current state via .getValue().
3. Why This Topic Matters
BehaviorSubject is the backbone of local state management in Angular applications:
- State Synchronization: If you have an authenticated user profile, a shopping cart, or a dark mode toggle, multiple parts of your UI must know the current state *immediately* upon mounting. A BehaviorSubject ensures components don't start with blank templates or load stale default states.
- Synchronous Checks: Sometimes you need to know the state right now without setting up an asynchronous callback (e.g., checking if a user is logged in inside a route guard).
BehaviorSubject.getValue()lets you read state instantly.
4. Real-World Analogy
Think of a BehaviorSubject like a **Digital Thermostat on the wall**:
- Initial State: The thermostat is never blank; when it turns on, it immediately measures and displays a starting temperature (e.g. 72°F).
- Cached Value: It continuously holds the current room temperature.
- Immediate Delivery: If you walk up to the thermostat and look at it (subscribing), you don't have to wait for the heater to cycle or wait for the temperature to change. It immediately tells you the current temperature (72°F) the instant you look at it.
5. Core Concepts
Below is a comparison of how Subjects and BehaviorSubjects manage data delivery:
| Feature | Subject | BehaviorSubject |
|---|---|---|
| Initial Value | No starting value required. | Mandatory initial value upon construction. |
| Late Subscriber Emits | Nothing (misses past events). | Emits the current cached value immediately. |
| Synchronous Read | No. Cannot read current value directly. | Yes. Via `.getValue()` or `.value`. |
6. Syntax & API Reference
This is the syntax for managing state with BehaviorSubject:
7. Visual Diagram
This timeline displays value caching and immediate emission to late subscribers:
8. Live Example — Full Working Code
Below is a complete theme-switching service that stores user preferences reactively, updating styling in independent components:
What just happened? The ThemeService starts with a value of `'light'`. When ThemeContainerComponent mounts and subscribes to theme$, it immediately receives `'light'` and sets its background. Clicking the button calls toggleTheme(), pushing `'dark'` into the stream. Both components receive the notification and redraw.
9. Interactive Playground
Try It Yourself Challenges:
- Modify
ThemeServiceto load the initial theme string dynamically from browserlocalStorageif available, falling back to 'light'. - Add a new component that does not subscribe to
theme$but usesthemeService.getCurrentTheme()in a method to show how it extracts state synchronously.
10. Common Mistakes
| Mistake | Why it happens | Wrong | Correct |
|---|---|---|---|
| Mutating Arrays/Objects in State | Pushing elements directly onto a state array does not update the reference, so change detection ignores it. | const cur = bs.value; |
const cur = bs.value; |
| Failing to Provide Initial Value | Instantiating a BehaviorSubject without providing a default value results in compiler errors. | new BehaviorSubject<string>() |
new BehaviorSubject<string>('init') |
11. Best Practices
- Enforce Immutability: Always push new object references (via array spreading or cloning) into
.next()to avoid silent data mutations. - Keep Subjects Private: Never expose
BehaviorSubjectdirectly. Expose it viaasObservable()to ensure write permissions are restricted. - Avoid Over-using getValue(): Rely primarily on stream subscriptions rather than reading values synchronously with
.getValue()to maintain reactive data flows.
12. Browser Compatibility/Requirements
BehaviorSubjects are plain JavaScript classes and perform consistently in all modern web browsers.
13. Interview Questions
Q1: How does BehaviorSubject handle a new subscriber differently than a standard Subject?
Answer: A standard Subject does not cache values; it only emits values to observers currently subscribed when next() is called. A BehaviorSubject caches the last emitted value and emits it immediately to any new observer upon subscription.
Q2: Why is it recommended to use spread operators when updating state stored in a BehaviorSubject?
Answer: Direct mutation (e.g., arr.push()) alters data in place without changing the array reference. Angular change detection relies on object reference changes. Creating a new array reference (e.g., [...arr, item]) guarantees change detection picks up the update.
14. Debugging Exercise
Identify why the UI does not refresh when adding items to this list store:
Diagnosis: The list reference retrieved by itemsSubject.getValue() is mutated in place using list.push(item). Passing the mutated reference to .next() emits the exact same array reference. Angular components bound via the async pipe won't detect the change.
Fix: Create a new array instance using the ES6 spread operator to trigger a reference change.
15. Practice Exercises
Exercise 1: Implement an authentication state store
Build an AuthService containing user profiles and authentication status using a BehaviorSubject. Add login() and logout() methods. Build an Angular Route Guard that uses .getValue() to inspect authentication status synchronously during transitions.
16. Scenario-Based Challenge
The Multi-Step Checkout Form Store:
You are implementing a 3-step wizard checkout flow. Create a CheckoutFormStoreService that uses a BehaviorSubject to maintain progressive form inputs (shipping details, payment methods, confirmation). Design the service to ensure components can subscribe to read active progress at any step and load filled data if navigating backwards.
17. Quick Quiz
Q1: What are the two main characteristics of a BehaviorSubject?
A) No default value required; discards all past events
B) Requires initial value; caches and emits the last value to late subscribers
C) Non-cancellable; only processes single emissions
Answer: B — BehaviorSubjects require an initial value and cache emissions to play them to late subscribers.
18. Summary & Key Takeaways
- BehaviorSubjects represent states with historical cache and default values.
- New observers get the last emitted value immediately on subscription.
- You can synchronously inspect the active state with the
.getValue()method. - Exposing private BehaviorSubjects as read-only observables ensures correct architecture.
19. Cheat Sheet
| API / Method | Purpose |
|---|---|
new BehaviorSubject(val) |
Creates a BehaviorSubject with the specified initial value. |
subject.getValue() |
Synchronously retrieves the current state value. |
subject.next(newVal) |
Updates the cached state and broadcasts it to all subscribers. |