Observer Pattern In JS (RxJS at its basic)

ReactiveX or Reactive Extensions

RxJS is javascript implementation of Reactive Extension, it allows you to use Observer pattern in your web app.

RxJS is much more than what we are going to learn here, that is why the name (RxJS at its basic)

At its basic Observer pattern lets you subscribe to changes in subject with the help of events.

First we will focus on Subscribers and Events:

Subscriber:

Object has a property named subscribers to manage subscribers who want to listen to changes in our subject.

let subscribers= []

When a subscriber arrives, object adds it to subscribers and when a subscriber wants to unsubscribe, subject should remove subscriber from his subscribers

Event:

When data of subject is changed or when subject wants to inform changes to its subscribers it triggers an event. When an event is triggered, it is duty of a subject to inform every subscriber about the event. To do that we just have to iterate over all subscribers and execute their callbacks.

this.subscribers.forEach(function(fn) {
  fn(data);
});

So let's come to the final step of a Observable Pattern and that is Subject

Subject:

A subject which just an Object which informs its changes to all of its subscribers.

Here is an example of a general Observable Subject will extend.

export default class ObservableSubject {
  constructor() {
    this.subscribers = [];
  }

  getAllSubscribers() {
    return this.subscribers;
  }

  /**
   * Register to a event
   *
   * @param {Function} fn subscriber callback function
   */
  subscribe(fn) {
    this.subscribers.push(fn);
  }

  /**
   * Un-Register from a event
   *
   * @param {Function} fn callback function to be removed
   */
  unsubscribe(eventName, fn) {
    if (this.subscribers) {
      for (var i = 0; i < this.subscribers.length; i++) {
        if (this.subscribers[i] === fn) {
          this.subscribers.splice(i, 1);
          break;
        }
      }
    }
  }

  /**
   * Trigger a event with data
   *
   * @param {Object} data optional data to pass
   */
  trigger(data) {
    if (this.subscribers) {
      this.subscribers.forEach(function(fn) {
        fn(data);
      });
    }
  }
}

So, let's make a observable User which will extent ObservableSubject

import ObservableSubject from "./ObservableSubject";

export default class User extends ObservableSubject {
  setName(name) {
    this.name = name;
    this.trigger({
      user: {
        name: this.name,
        email: this.email
      }
    });
  }

  getName() {
    return this.name;
  }

  setEmail(email) {
    this.email = email;
    this.trigger({
      user: {
        name: this.name,
        email: this.email
      }
    });
  }

  getEmail() {
    return this.email;
  }
}

And this concludes a basic RxJs implementation.

Complete example is over at codesandbox

Comments (3)

Siddharth Vishvanath (Sid)'s photo

Can i use this to sync changes between tabs within the same window.

Tej Pratap Singh's photo

No, this code is just to show how we can design our app using Observer Pattern.

To sync data across tabs, you need to either bind listeners to IndexedDB or Use WebRTC within local tabs.