Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome back to our series on RxJS.
In our last lesson, we explored the Subject, establishing its dual nature as both an Observer and an Observable, which allows it to multicast values. We saw this is essential for sharing a single event source with multiple parts of an application.
However, a standard Subject has no memory. If a component subscribes after a value has been emitted, it misses that value and must wait for the next one. For managing application state, this is often not what we want; we usually need access to the current state at any time.
Today, we'll address this by focusing on a specialized and widely-used type of Subject: the BehaviorSubject. This is the cornerstone of most RxJS state management patterns.
Our learning outcome is to apply BehaviorSubject to manage state with an initial value and synchronous access to the current value.
1. The "Current Value" Problem
Let's start by seeing the limitation of a regular Subject in a state management context. Imagine you have a Subject that holds the current user's theme preference ('light' or 'dark'). If a component subscribes after the initial theme has been set, it won't receive the value and won't know which theme to render.
This short video clearly demonstrates this issue and the suboptimal workarounds one might attempt.
BehaviorSubject | RxJS TUTORIAL
This video from Academind starts by showing why a regular Subject is insufficient for representing state that needs an initial or current value.
Watch from the beginning until 01:47. Notice how the regular Subject doesn't emit anything upon subscription and requires manual, imperative calls to set an initial state after the subscription is established.
As you saw, manually pushing a "default" value after subscription is clumsy. We need a more elegant solution, and that's precisely what BehaviorSubject provides.
2. Introducing BehaviorSubject: A Subject with State
A BehaviorSubject is a special type of Subject that is designed to always represent a "current value." It has two key characteristics that distinguish it from a regular Subject:
- It requires an initial value: When you create a
BehaviorSubject, you must provide a starting value. - It emits its current value to new subscribers: Any new subscriber will immediately receive the most recent value that was emitted.
This makes it a "stateful" observable. It doesn't just pass events; it holds and represents a value over time.
Let's see how this directly solves the problem we just discussed.
BehaviorSubject | RxJS TUTORIAL
Continuing with the same Academind video, this clip introduces BehaviorSubject as the direct solution.
Watch from 01:47 to 02:35. Pay close attention to how the initial value is passed in the constructor and how it's immediately available to the subscriber without any extra calls.
3. The BehaviorSubject API: .value and .next()
Interacting with a BehaviorSubject for state management primarily involves two aspects: reading the state and updating the state.
Updating State: next()
Just like a regular Subject, you update the value of a BehaviorSubject by calling the .next() method with the new value. This will push the new value to all current subscribers.
Reading State: subscribe() vs. .value
This is where BehaviorSubject truly shines for state management. You have two ways to read its value:
-
Asynchronous / Reactive Read (
subscribe()): This is the standard Observable way. You subscribe to theBehaviorSubjectto receive its current value and all subsequent updates. This is how you build reactive UI components that automatically re-render when the state changes. -
Synchronous / Imperative Read (
.value):BehaviorSubjectexposes a special property,.value, which gives you a snapshot of the current value at the exact moment you access it. This is incredibly useful for logic where you need to read the state without necessarily subscribing to future changes (e.g., in an event handler that needs to know the current state to perform a calculation).
The following article provides a clear code example of creating a state store and using both .value to read the current state and .next() to update it.
Simplifying State Management in React Using RxJS
This article from Dhiwise, 'Simplifying State Management in React Using RxJS', contains a concise code snippet that perfectly illustrates the read/update cycle of a BehaviorSubject.
Find the section titled 'Exploring RxJS Subjects and BehaviorSubjects'. Focus on the code block. Notice how the incrementAction function uses stateSubject.value to read the current count before calculating the new value and pushing it with stateSubject.next().
Here's a conceptual example of how you might structure a simple state service, incorporating the asObservable() pattern we discussed in the last lesson:
import { BehaviorSubject } from 'rxjs';
class CounterStore {
// The BehaviorSubject is private, initialized with a starting value of 0.
private readonly _count = new BehaviorSubject<number>(0);
// Expose the public, read-only Observable stream for reactive updates.
public readonly count$ = this._count.asObservable();
// A public getter for synchronous reads.
public get currentCount(): number {
return this._count.value;
}
// A public method to imperatively update the state.
public increment() {
const newCount = this.currentCount + 1;
this._count.next(newCount);
}
}
const store = new CounterStore();
// Reactive subscription (e.g., in a React component)
console.log('Subscribing to count$...');
store.count$.subscribe(count => console.log(`Reactive update: Count is ${count}`));
// Imperative update
console.log('Incrementing...');
store.increment();
// Synchronous read (e.g., in a different part of the app)
console.log(`Synchronous read: The current count is ${store.currentCount}`);
// --- Console Output ---
// Subscribing to count$...
// Reactive update: Count is 0 <-- Immediately gets the initial value
// Incrementing...
// Reactive update: Count is 1 <-- Receives the update via .next()
// Synchronous read: The current count is 1 <-- Reads the latest value via .value
4. BehaviorSubject in a React Context
Now let's see how these concepts translate to a practical React application. Your goal is to use RxJS for real-world state management, and BehaviorSubject is the perfect tool for creating a global state store.
This video demonstrates creating a BehaviorSubject to hold a list of Pokémon, fetching the data, and updating the subject.
React + RxJS = Reactive Global Goodness
In this video, 'React + RxJS = Reactive Global Goodness', Jack Herrington builds a simple but effective global store using BehaviorSubject.
Watch from 03:18 to 06:11. Observe how he: Instantiates a BehaviorSubject with an initial empty array. Fetches data from an API. Uses .next() to push the fetched data into the BehaviorSubject, effectively updating the global state. Subscribes within a useEffect hook to see the values arrive (first the initial empty array, then the full data).
This example perfectly encapsulates the core pattern:
- Initialize: Create a
BehaviorSubjectto serve as your single source of truth. - Update: Use
.next()to feed new state into it, often from asynchronous operations like API calls. - Subscribe: Components listen to the subject's stream to react to state changes.
We will explore the best way to subscribe in React in a later module to avoid issues like "tearing," but this example clearly shows the fundamental data flow.
Conclusion
You've now learned about the most important tool in the RxJS state management toolbox. The BehaviorSubject bridges the gap between a simple event bus and a full-fledged state container.
Key Takeaways:
BehaviorSubjectis a statefulSubjectthat requires an initial value.- It guarantees that any new subscriber will immediately receive the most recent value.
- You can read the current state synchronously using the
.valueproperty, which is invaluable for imperative logic. - You update the state by calling
.next(newValue). - It is the foundation for creating centralized, reactive state stores in applications.
Next Up:
While BehaviorSubject remembers only the single most recent value, what if you need a history of values? In the next lesson, we will explore ReplaySubject, another variant that can buffer and "replay" a specified number of past emissions to new subscribers. This is useful for scenarios like caching recent events or implementing undo/redo functionality.