How to learn RxJS FAST! in 2024?

How to learn RxJS FAST! in 2024?

Updated:
Published:

You don't have to be an expert in RxJS to get started with writing reactive code. By the application of the Pareto principle, you will learn the most important concepts within minutes. So, nothing can stop you creating your first reactive JavaScript / TypeScript app!

Cover image

If you look at the API reference , you see that it is quite a complex library. So let’s apply the Pareto principle (aka. 80/20 rule) to get started.

Which topics are the most relevant for learning RxJS?

Here is a list of the most important topics you should dive into for starting off with RxJS:

You don’t have to know and understand every detail of those topics to write good RxJS code. Especially the daunting list of operators you should know of at the beginning, will be much shorter than the list in the RxJS guide .

Let’s first have a high-level look at the library.

The absolute basics

RxJS is written in TypeScript, and all the examples listed here as well. But nothing would stop you from using it in plain JavaScript as well.

To make any sense of RxJS, you should first understand how Observables, Observers, and Subscriptions play together.

RxJS provides an implementation of the Observer Pattern.

There is the Observable (consider it to be a value producer) and the Observers (value consumers).

One or multiple Observers can subscribe to an Observable. By doing so, a Subscription is created.

Operators can be used to intercept values and state changes of an Observable.

Observables

An Observable instance has three states: open, completed and errored. In the open state, it can emit a random number of values. In the completed and error state, it will never emit any values. Observables are lazy and only produce values, if there is an Observer subscribed to it.

Let’s take a look at a very basic example:

const oneValue$ = new Observable<number>(
  (observer) => {
    observer.next(42);
    observer.complete();
  },
);

If an Observer subscribes to oneValue$ , the Observable will send one value and then immediately complete.

This behavior is quite similar to that of Promise.resolve(42) , apart from that Observables are lazy.

Hot vs. Cold Observables

In the RxJS context, you will sometimes come across the distinction between hot and cold Observables.

Every time an Observer gets subscribed to an Observable instance, the callback passed to the Observable constructor will be called.

The implementation of this callback determines whether the Observable is hot or cold.

If the callback instantiates a value producer, or produces the values itself (see Example 1), this producer will be created for every subscription. Such an Observable is called “cold”.

If, on the other hand, the callback delegates to an external value producer, the Observable is called “hot”. An example could be listening to ClickEvent s from a button.

const btn = document.querySelector("button")!;
const btnClicks$ = new Observable<Event>(
  (o) => {
    const handler = (e: Event) => o.next(e);
    btn.addEventListener("click", handler);
    return () => {
      btn.removeEventListener("click", handler);
    };
  },
);

Observers might miss emitted values, if they subscribe to a hot Observable later than the producer starts producing values.

Observers

Observers implement this interface:

interface Observer<T> {
  next?: (value: T) => void;
  error?: (err: any) => void;
  complete?: () => void;
}

An Observer is passed to an Observable via the subscribe() method. The Observer will be notified about emitted values via the next() method. State changes of the Observable will be emitted via the error() and completed() methods.

Subscriptions

The Observable’s subscribe() method will return a Subscription . This subscription can be cancelled by calling its unsubscribe() method.

You can aggregate multiple Subscriptions via the add() method. If this aggregated Subscription is cancelled, all Subscriptions will be cancelled.

It’s important to have an eye on the Subscriptions within your app. Otherwise, you can run into memory leaks.

Operators

Operators are the most powerful part of RxJS. They allow you to react to value or state change emissions.

const btn = document.querySelector("button")!;
const btnClicks$ = fromEvent(btn, "click");
const clicksWithin10Seconds$ = btnClicks$.pipe(
  takeUntil(timer(10000)),
  count(),
);

In this example, we use a factory that provides a btnClick$ Observable, equivalent to the last example. Then we pipe() the values through the Operators reduce() and takeUntil() .

Operators shipped with RxJS

There are currently 109 Operators provided by RxJS version 7.8.1. A nice list can be found in the RxJS Docs . Here is a list of some Operators that I use quite frequently:

You can read more about this topic by reading my Post Mastering RxJS Operators .

Building your own Operators

You can easily build your own operators. An Operator is simply a factory for a function that takes an Observable and returns an Observable.

const select =
  <T, K extends keyof T>(key: K) =>
  (source$: Observable<T>) =>
    source$.pipe(
      map((value) => value[key]),
      distinctUntilChanged(),
    );

This Operator will take a property value from each emitted value of the source$ Observable and only emit it, if changed since the last emission. This example only combines uses pre-existing operators, but you can do anything you want inside the Operator function. You can hold local state, for example, what is done by some preset Operators like scan or buffer do.

Subjects

Subjects are implementations of Observables as well as Observers. In contrast to plain Observables, they multicast to all their subscribed Observers.

There are 4 different Subjects available in RxJS:

Default Subject

The Subject can be used to simply multicast values to its Observers.

const subject = new Subject<number>();
subject.subscribe(console.log);
subject.next(1); // log: 1
subject.next(2); // log: 2
subject.subscribe(console.log);
subject.next(3); // log: 3, 3

BehaviorSubject

If you want to multicast and memorize a value that should be emitted whenever a new Observer subscribes, you can use a BehaviorSubject . It always has a current value, so it has to be initialized with an initial value.

const subject = new BehaviorSubject<number>(0);
subject.subscribe(console.log); // log: 0
subject.next(1); // log: 1
subject.next(2); // log: 2
subject.subscribe(console.log); // log: 2
subject.next(3); // log: 3, 3

This Subject is practical when you aim to hold your application state on a central location, for example.

ReplaySubject

Occasionally, you want to cache multicasted values, but not have an initial value. Then you can give the ReplaySubject a try. It caches the last n messages for newly subscribed Observers.

const subject = new ReplaySubject<number>(2);
subject.subscribe(console.log);
subject.next(1); // log: 1
subject.next(2); // log: 2
subject.subscribe(console.log); // log: 1, 2
subject.next(3); // log: 3, 3

AsyncSubject

The AsyncSubject multicasts the last value it has received when it completes.

const subject = new AsyncSubject<number>();
subject.subscribe(console.log);
subject.next(1);
subject.next(2);
subject.subscribe(console.log);
subject.next(3);
subject.complete(); // log: 3, 3

Conclusion

With this short overview, you should be ready to get going with RxJS. You learn best, if you get your hands dirty, so I suggest you simply start coding and learn the Operators you need on demand.

Have fun with learning RxJS.