Create your own Redux with RxJS

Basic concepts of reactivity and the RxJS library, and how the Redux store can be implemented using RxJS.

Reactivity.

Reactive programming is something you might have heard of, but might not know how to approach yet. In very simple words, it's a programming paradigm like 'Functional programming' or 'Object-oriented programming' which focuses on a different aspect, namely, how to write programs that automatically do something when something else happens.

Event handlers in JS are naturally reactive and demonstrate this well:

const someDiv = document.getElementById('my-div')

function handleClick() {
  doSomeFancyStuff()
}

someDiv.addEventListener('click', handleClick)

They listen for events and automatically trigger a handler (in this case handleClick) when the event happens, which in turn does something (in this case, some fancy stuff).

In fact events are a nice way to model things that are yet to happen or finish (like asynchronous code returning a value).

A Note on Sequences:

An array is the simplest kind of collection of data. It is a sequence of things; which is as simple as a collection can get.

Arrays provide ways to deal with collections easily. You are probably familiar with loops that let you process an array by iterating over its elements.

Also, many languages like JavaScript, provide higher order functions like map and filter, that let you pass a function that will be applied to each element in the array.

Adding 2 to an array of numbers is easy with Array.prototype.map

const arrayOfNumbers = [1, 2, 3, 4]

const add2 = x => x + 2

const arrayOfSlightlyBiggerNumbers = arrayOfNumbers.map(add2)

Array.prototype.map returns a new array and does not mutate the old one. Moreover, the array prototype methods can be chained.

const todoList = [
  { id: 1, text: 'Learn ES6', done: true },
  { id: 2, text: 'Learn RxJS', done: false },
  { id: 3, text: 'Buy groceries', done: false },
]

const isCompleted = todo => todo.done
const getTodoText = todo => todo.text
const startsWithLearn = text => text.startsWith('Learn')

const completedLearningTaskCount = todoList
  .filter(isCompleted)
  .map(getTodoText)
  .filter(startsWithLearn)
  .length

These two facts let us deal with arrays in a clean, declarative and functional way.

Wouldn't it be great if we had a way to deal with events and asynchronous operations in a clean and familiar way, much similar to the one mentioned above?

Well, this is exactly what ReactiveX does. ReactiveX is a language independent specification that describes an API (functions and interfaces) that helps us deal with all sorts of things by treating them as sequences of events.

RxJS is the JavaScript implementation of ReactiveX.

Why ReactiveX matters.

ReactiveX lets us model just about everything as a stream of events.

An event stream is very similar to an array, except it's asynchronous. So each element in the stream arrives as and when it is generated.

A sequence of mouse clicks can easily be visualised as an event stream.

So can an API call, although it is a stream of a single event. And even regular arrays!

In the end, ReactiveX and RxJS provide a uniform way to deal with asynchronous and synchronous data using a huge number of functions called operators.

RxJS Concepts.

RxJS is a huge library, and is slightly intimidating. It can be overwhelming to go through the documentation and figure out where to start.

This article aims to give you the bare minimum information to get quickly started with RxJS, and hopefully to absorb the idea and whole point of reactivity.

So here goes.

RxJS has four things at its core:

  1. Observables
  2. Subscriptions
  3. Observers
  4. Operators

- Observables are streams (or sequences) of events.

- Subscriptions trigger code when an observable generates a new event (addEventListener behaves like a subscription in this way).

- Observers are what subscribe to the observable (usually they take the form of a handler, or a function, just like the click handler handleClick we saw earlier).

- Operators are functions which process each event coming from the observable and generate a new observable stream in the process (a bit like map, filter, reduce and other higher order functions).

And that's it. That's the core of RxJS.

This sandbox demonstrates all four of these things. It creates an observable from the clicks coming from a button.

Some Advanced Concepts.

Apart from the four concepts we saw earlier, you'll need to know a couple more things before implementing you own Redux store implementation:

  1. Subject
  2. BehaviourSubject

A Subject is like an Observable in many ways, but provides a way for multiple observers to listen easily. Think of them as event emitters.

BehaviourSubject is exactly like a Subject, except it stores the last emitted event. So you could consider it to be stateful.

This statefulness is exactly what we need to implement Redux's store.

And finally, the part you've been waiting for

Redux is a popular state management library, whose main philosophy is that an app's state must come from a single source of truth, named the store.

The store in Redux provides three methods:

  1. getState: a way to get the store's state at any given time
  2. dispatch: a way to trigger a change in the store state using actions
  3. subscribe: a way to reactively subscribe to state changes in the store

Let's try to implement the store and this API using RxJS. We'll use BehaviourSubject to do that.

Let's start off with a class to define our store interface.

class Store {
  constructor(reducer, initialState) {
    this.state = initialState
    this.reducer = reducer
  }

  // Gives us the state at that instant
  getState = () => {}

  // Takes an action and causes a change in the store state
  dispatch = (action) => {}

  //Calls the observer whenever the state changes
  subscribe = (observer) => {}
}

Let's first initialise the state. Since we're using BehaviourSubject, we just initialise the state to a new BehaviourSubject with the initial state

constructor(reducer, initialState) {
  this.state = newBehaviourSubject(initialState)
  this.reducer = reducer
}

The state of the BehaviourSubject at any given time can be retrieved easily using the value property. This gives us a natural way to implement getState

getState = () => {
  this.state.value
}

The dispatch is a bit more complex. It takes an action and changes the state according to the reducer. So we can get the next state by calling the reducer with the action. We can then update the state by calling BehaviourSubject.next

dispatch = (action) => {
  const nextState = this.reducer(this.getState(), action)
  this.state.next(nextState)
}

The subscribe method is almost as simple as getState. Since one can easily subscribe to a BehaviourSubject by using its subscribe method, we do just that.

subscribe = (observer) => {
  this.state.subscribe(observer)
}

And that's it!

We have created a class that defines the Redux store.

Here's the full implementation, used in a simple todo list app written in React. Notice how I just replace Redux's createStore with my own, and everything just works.

It's important to note that a lot of error handling and special cases have been ignored here, but it really showcases how powerful RxJS can be.

Conclusion

Reactive programming is an incredibly powerful programming model and can help us deal with something as complex as event handling with ease.

RxJS is a library that helps us write apps in such a way. It's powerful enough to let you implement your own Redux store, or any other state management mechanism with it.

It's also worth noting that the reason I was able to swap in my own store so easily in the todo app is because Redux's own API is simple and well defined. Good API design can go a long way in making maintainable software that may require you to swap out components in favour of others that better suit the changing requirements.

Further reading

2017 © All rights reserved. Sahu Soft India Pvt Ltd.