The Differences Between Subjects and BehaviorSubjects in RxJS

If you’re just starting to dive into Angular you’ve probably quickly realized that it’s a lot different from the other major front end frameworks such as React and Vue. Angular is a “convention over configuration” framework in the sense that the Angular development team has designed the framework in such a way to more or less force you into following a certain set of rules in order for it to work properly. (I’ve personally found Vue to be a happy medium in between Angular and React in this sense)

If you’re a less experienced front end developer and are looking to up your JavaScript game, forcing yourself to create a few Angular apps is a great way to really level up your JS skill set. React and Vue do a great job at abstracting things away so some things that happen just seem like magic. Angular still has abstractions however not to the extent of the other two so you’re forced to roll up your sleeves and up your JS game. (This is really just me saying the learning curve for Angular is more demanding that any other front end framework.)

Angular ships with RxJS and it’s a complete paradigm shift in that you no longer simply rely on fetch() or axios.get() to handle your HTTP requests in your app. RxJS instead allows us as developers to subscribe to a data stream and listen to it for any changes while the subscription is active. To be fair, you can implement RxJS in either a React or Vue app, but neither ships with it out of the box so it’s up to you the install it as a dependency and use it (Convention over configuration… Remember?)

Subjects and BehaviorSubjects are observables that can emit their values to multiple observers via subscriptions. On the surface we’re almost inclined to think that both a Subject and a BehaviorSubject do the same thing, but the differences are subtle and can make a pretty big impact on your application.

In simple terms: A Subject does not need to have an initial value when being created but a BehaviorSubject requires an initial value when being created. Therefore, a Subject will only emit upcoming values, whereas a BehaviorSubject will emit one previous value and all upcoming values.

Let’s set the stage for a pretty common example front end developers handle on a routine basis: showing or hiding a modal to let the user edit settings. As you can imagine, this is a good use case for a BehaviorSubject because we’ll want to set the initial value of “showModal” to “false”.

// src/app/services/modal.service.ts

import { BehaviorSubject } from 'rxjs';

export class ModalService {
  showModal = new BehaviorSubject<boolean>(false);

  toggleModal() {
    this.showModal.next(!this.showModal.value);
  }
}

Had we not passed in a boolean value to the BehaviorSubject’s constructor, TypeScript would have immediately given us an error indicating that one argument was expected, but we’ve given it 0.

The above example sets up an Angular service that we can then import into any other component and utilize the toggleModal() method. Any subscriptions to this observable therefore will receive an initial value of “false” since BehaviorSubjects allow us to track and initial value and any subsequent changes in the future.

On the other hand, a Subject does not require an initial value. In sticking with the modal example from above, a good parallel use case for a Subject would be if upon the user opening a modal in the app we want to display a form containing the app’s settings that they can modify and then save. Let’s take a look:

// src/app/services/settings.service.ts

import { Subject } from 'rxjs';
import { HttpClient } from '@angular/common/http';

export class SettingsService {

  appSettings = new Subject<Settings>();

  constructor(private http: HttpClient) {}

  getAppSettings() {
  this.http.get<Settings>('https://www.myapp_api.com/settings')
    .subscribe((settings: Settings) => {
      console.log(settings, 'Settings received from API call.');
      this.settings.next(settings);
    )};
  }

}

Sure, there’s a little more code than the previous BehaviorSubject example but most of it is simply boiler plate code in order to set things up. The key takeaway here should be that when we create a new Subject, we do not have to supply its constructor method with an argument to set the initial value upon creation. We only have to indicate what kind of value will be occupying it in the future, which in this case is an object of type “Settings”.

We’ve defined a getAppSettings() method that we can again import into any other component and fetch our user’s app settings from the back end and then once the request comes back we will push the new value via the next() method and any subscriptions made will finally receive their first value. We will then have access to the app settings, now stored with the label appSettings, be it an object of settings or an array.

So what’s the tl;dr version of all of this? Pretty simple, actually:

Use a BehaviorSubject if you need an initial value (The modal example above is a really good example) and use a Subject if you do not need an initial value.