If you’ve heard someone describe RxJS, they’ve likely started by saying it was “powerful” and “revolutionary.” While this is true, you don’t need to be a 33rd degree functional reactive programmer to use RxJS. It can also be a simple, easy-to-use library.

This article is intended to be a primer to help junior developers start to learn RxJS. It covers a common pattern used to create events with RxJS, and provides a walkthrough of an example application I’ve pushed to Github.

The Minimum You Need to Know

RxJS can be thought of as an event handling library. Their documentation states,

“Think of RxJS as Lodash for events.”

I think that learning RxJS would have been simpler if I had first encountered an example of how to replace a plain JavaScript click event with RxJS. This won’t necessarily teach you the benefits of RxJS in a complex application, but it should make articles dealing with the pros, cons, and advanced features easier to understand.

Basic Terms

Listed below are a few terms you’ll need to get familiar with. Each of these are classifications of variables that you’ll be creating for RxJS, though the terms also reference long-standing programming patterns. My definitions below pertain to their RxJS implementation.

  • Observable
    • Functions that return values over time
      • These can and generally are chained together, but don’t have to be
    • Sometimes called “the stream”
  • Observer
    • A grouped set of functions that handle emitted values from your observables
  • Subscription
    • A connection between an observable and an observer
      • Subscriptions can start or end a stream
      • Think, “an observer subscribes to an observable”

Basic Pattern

When using RxJS, you’ll frequently do something like this:

1. Create an Observable

You’ll set up something very similar to a plain DOM event.

let button$ = Observable.fromEvent(button, 'click');

2. Use RxJS Operator Functions to Modify the Observable

You’ll use operators from the RxJS library to customize the original event.

let filteredButton$ = button$.filter((e) => {
  return e.target.className === 'let-me-through';
});

3. Create an Observer to Handle Values Passed On At the End of An Observable + Operator Chain

You’ll create a final callback function as an object, with up to three keys. The first handles “normal” or successful stream values, the second handles errors, and the last function runs when the stream has completed.

When a stream has completed, it expects that it’s emitted its last value. The stream has basically been turned off. In my example application, the complete function would never be triggered.

let buttonObserver = {
  next: (x) => {
    console.log('The stream emitted a value! You should have done something more useful than logging to the console!');
  },
  error: (err) => { console.error(err); },
  complete: () => { console.log('Cashing out my 401k...'); }
};

4. Turn the Observable on with a Subscription

let buttonSub = filteredButton$.subscribe(buttonObserver);

My Example Application

I created an example application that has one page and one button. When you click on the button, it switches css classes on the body element, changing the background and text color. I created one version using RxJS, and one using plain JavaScript.

Check out my example application on Github.

Getting Started

I started both versions the same, with DOM references for the button and body, and a variable to track state (in this case, whether the light is on or off).

// DOM references
let button = document.querySelector('button');
let body = document.querySelector('body');

// State
let lightsOn = true;

Event Listeners and Observables

Vanilla JavaScript: Event Listener

In the plain JavaScript version, I created the event listener below. It attaches a click event to the button, and calls a lightsClicked function.

// Event listener
button.addEventListener('click', lightsClicked, false);

RxJS: Observables

In RxJS, you’ll use methods provided by the RxJS library to capture events, make modifications, and pass on the values you need. In effect, you can compose custom events by combining these methods (called RxJS operators).

In the code segment below, button$ is an observable stored in a variable that watches button for click events, and passes the event to scan. scan toggles the value of lightsOn, and remembers the value for the next click event.

on$ is an observable, continuing from $button, with a filter operator that takes the value passed from scan and filters for instances where scan returned true. When true, mapTo packages an object with the body DOM reference, the name of the class to be added, and the name of the class to be removed.

out$ works the same way as on on$, but filters for instances where scan passed a value of false.

// Observable
// Streams of values, transformed with RxJS operators:
// scan, filter, mapTo
// http://stackoverflow.com/questions/34533158/create-a-toggle-button-with-rxjs
let button$ = Observable.fromEvent(button, 'click')
  .scan ((state, event) => {
    return !state;
  }, lightsOn);

let on$ = button$
  .filter(x => x)
  .mapTo({
    'el': body,
    'classToAdd': 'lights-on',
    'classToRemove': 'lights-out'
  });

let out$ = button$
  .filter(x => !x)
  .mapTo({
    'el': body,
    'classToAdd': 'lights-out',
    'classToRemove': 'lights-on'
  });

Callbacks, Observers, and Subscriptions

Vanilla JavaScript: Callbacks

In the vanilla JavaScript version, we’d need to set up a function, or a series of function, as a callbacks for the event.

In this instance, I created:

  1. lightsClicked
    1. Calls toggleLights with the event and the lightsOn variable
    2. Switches state, or toggles lightsOn between true and false
  2. toggleLights
    1. Calls flipClass based on the value passed for lightsOn
    2. Packages the body DOM reference, class name to add, and class name to remove as parameters for flipClass
  3. flipClass
    1. Uses helper functions to remove and add the specified classes to the body DOM reference from toggleLights
// Event handling functions
function lightsClicked(e) {
  toggleLights(e, lightsOn);
  lightsOn = !lightsOn;
}

function toggleLights(e, isOn) {
  if (isOn) {
    flipClass({
      'el': body,
      'classToAdd': 'lights-out',
      'classToRemove': 'lights-on'
    })
  } else {
    flipClass({
      'el': body,
      'classToAdd': 'lights-on',
      'classToRemove': 'lights-out'
    })
  }
}

function flipClass(x) {
  addClass(x.el, x.classToAdd);
  removeClass(x.el, x.classToRemove);
}

RxJS: Observers and Subscriptions

In the RxJS version, I wrote an observer, and wired it to the observable with a subscription.

I made two subscriptions, one wired to the on$ observable and one wired to the out$ observable. Both use lightsObserver whenever a value is emitted. The next function is essentially the same as flipClass in the vanilla JavaScript example.

// Observer
// What to do with the streams
let lightsObserver = {
  next: (x) => {
    addClass(x.el, x.classToAdd);
    removeClass(x.el, x.classToRemove);
  },
  error: (err) => { console.error(err); },
  complete: () => { console.log('stream complete'); }
};

// Subscription
// Connection between observer and observables
let onSub = on$.subscribe(lightsObservable);
let outSub = out$.subscribe(lightsObservable);

Misc. Weird Things

The RxJS library is big. It’s commonly recommended not to send the entire library to a client. Instead, if you’re using a module loader like SystemJS, it’s standard to only list and import the exact operators you need. For this example application, we’d import:

import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/scan';
import 'rxjs/add/operator/mapTo';

Helpful Resources

Egghead has a lot of courses covering the fundamentals of RxJS. This has been my go-to resource for RxJS. However, it’s a subscription service. If you sign-up by following my link, I think I get a kickback (you probably don’t get anything).

“The introduction to Reactive Programming you’ve been missing” is a very commonly recommended article as a primer for RxJS and reactive programming.

RxMarbles has marble diagrams to demonstrate how many RxJS operators.

This ngAir podcast features Ben Lesh and Tracy Lee, who work on developing the current version of RxJS.

The observable cheat sheet explains RxJS as a cartoon.