Create your own Redux with RxJS

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

Author

Rishabh Karnad
Rishabh KarnadAssociate Engineering Manager

Date

Feb 11, 2020

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

Book a Discovery Call

Recaptcha Failed.

Related Articles.

More from the engineering frontline.

Dive deep into our research and insights on design, development, and the impact of various trends to businesses.

From RFPs to Revenue: How We Built an AI Agent Team That Writes Technical Proposals in 60 Seconds
Article

Apr 9, 2026

From RFPs to Revenue: How We Built an AI Agent Team That Writes Technical Proposals in 60 Seconds

GeekyAnts built DealRoom.ai — four AI agents that turn RFPs into accurate technical proposals in 60 seconds, with real-time cost breakdowns and scope maps.

How We Built an AI System That Automates Senior Solution Architect Workflows
Article

Apr 6, 2026

How We Built an AI System That Automates Senior Solution Architect Workflows

Discover how we built a 4-agent AI co-pilot that converts complex RFPs into draft technical proposals in 15 minutes — with built-in conflict detection, assumption surfacing, and confidence scoring.

AI Code Healer for Fixing Broken CI/CD Builds Fast
Article

Apr 6, 2026

AI Code Healer for Fixing Broken CI/CD Builds Fast

A deep dive into how GeekyAnts built an AI-powered Code Healer that analyzes CI/CD failures, summarizes logs, and generates code-level fixes to keep development moving.

A Real-Time AI Fraud Decision Engine Under 50ms
Article

Apr 2, 2026

A Real-Time AI Fraud Decision Engine Under 50ms

A deep dive into how GeekyAnts built a real-time AI fraud detection system that evaluates transactions in milliseconds using a hybrid multi-agent approach.

Building an Autonomous Multi-Agent Fraud Detection System in Under 200ms
Article

Apr 1, 2026

Building an Autonomous Multi-Agent Fraud Detection System in Under 200ms

GeekyAnts built a 5-agent fraud detection pipeline that makes decisions in under 200ms — 15x cheaper than single-model systems, with full explainability built in.

Building a Self-Healing CI/CD System with an AI Agent
Article

Mar 31, 2026

Building a Self-Healing CI/CD System with an AI Agent

When code breaks a pipeline, developers have to stop working and figure out why. This blog shows how an AI agent reads the error, finds the fix, and submits it for review all on its own.

Scroll for more
View all articles