Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Managing State with BehaviorSubject

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:

  1. It requires an initial value: When you create a BehaviorSubject, you must provide a starting value.
  2. 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:

  1. Asynchronous / Reactive Read (subscribe()): This is the standard Observable way. You subscribe to the BehaviorSubject to receive its current value and all subsequent updates. This is how you build reactive UI components that automatically re-render when the state changes.

  2. Synchronous / Imperative Read (.value): BehaviorSubject exposes 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:

  1. Initialize: Create a BehaviorSubject to serve as your single source of truth.
  2. Update: Use .next() to feed new state into it, often from asynchronous operations like API calls.
  3. 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:

  • BehaviorSubject is a stateful Subject that 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 .value property, 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.

Can't find a good explanation? Sign up and we'll make it for you

Sign up