DEV Community

Fahad Heylaal
Fahad Heylaal

Posted on

Streaming Redux state as an Observable with RxJS

This article was first published on Medium.

headerimage

Redux is a great library for managing state in your applications. Combining it with React.js also gives your application a nice structure allowing you to benefit from various other tools built and supported by the community.

I also enjoy RxJS a lot. And the journey of building FrintJS has helped me embrace reactive programming even further. In this post, I will explore how we can stream the state from a Redux store using Observables.

Redux store

Let’s say we have a simple Redux store, that increments and decrements a counter value.

Reducer:

We can start by creating our reducer first:

const INITIAL_STATE = { value: 0 };

function counterReducer(state = INITIAL_STATE, action) {
  switch (action.type) {
    case 'INCREMENT_COUNTER':
      return Object.assign({}, {
        value: state.value + 1
      });
    case 'DECREMENT_COUNTER':
      return Object.assign({}, {
        value: state.value - 1
      });
    default:
      return state;
  }
}
Enter fullscreen mode Exit fullscreen mode

Store:

Now we can create a Store out of it:

import { createStore } from 'redux';

const store = createStore(counterReducer);
Enter fullscreen mode Exit fullscreen mode

Since your store is ready, you can start dispatching actions to it:

store.dispatch({ type: 'INCREMENT_COUNTER' }); // 1 (+1)
store.dispatch({ type: 'INCREMENT_COUNTER' }); // 2 (+1)
store.dispatch({ type: 'DECREMENT_COUNTER' }); // 1 (-1)
Enter fullscreen mode Exit fullscreen mode

Listening to state changes:

You can start listening to your state changes with simple callback:

const unsubscribe = store.subscribe(function () {
  const currentState = store.getState(); // { value: 1 }
});

// cancel listener when you don't need it
unsubscribe();
Enter fullscreen mode Exit fullscreen mode

State as an Observable

Listening to state changes with a simple callback can fit most applications’ needs. But if you are working with Observables already, it would make it easier for you to access the Redux state as a stream, which you can then connect with other Observables as you see fit.

But how can we convert the store to a state$ stream?

It is common convention in the community to end your variable or function name with a $ sign, if it is returning an Observable.

We will first do it the hard way, and then I will show you a lesser known API from Redux that works well with reactive libraries too.

Store’s state as an Observable:

Let’s create a function that accepts Redux store, and returns an Observable of its state.

import { Observable } from 'rxjs/Observable';

function getState$(store) {
  return new Observable(function (observer) {
    // more to follow...
  });
}

const state$ = getState$(store);

const subscription = state$.subscribe(function (state) { 
  console.log(state);
});
Enter fullscreen mode Exit fullscreen mode

Emit on new state changes:

We want the state$ to emit new values as the Redux store changes over time. So let’s add that logic to the function:

function getState$(store) {
  return new Observable(function (observer) {
    const unsubscribe = store.subscribe(function () {
      observer.next(store.getState());
    });
  });
}
Enter fullscreen mode Exit fullscreen mode

What we did above is start listening to the Redux store for changes, and whenever there is any change, we are emitting a new next event with the current state of store.

Emit an initial value:

But we cannot stop just here. Irrespective of when a state change occurs (via dispatching of actions), we want our state$ subscribers to be able to receive an initial value right after their subscription:

function getState$(store) {
  return new Observable(function (observer) {
    observer.next(store.getState());

    const unsubscribe = store.subscribe(function () {
      observer.next(store.getState());
    });
  });
}
Enter fullscreen mode Exit fullscreen mode

Now the subscribers will get an initial value right away, and as more state changes happen, they will keep receiving the new values over time.

Taking care of memory leak

We just need to make one more addition to our function. We have to make sure that as soon as our Observables are unsubscribed, the store listener is also cancelled.

We can do this by returning a function, which will be treated as an unsubscribe callback:

function getState$(store) {
  return new Observable(function (observer) {
    observer.next(store.getState());

    const unsubscribe = store.subscribe(function () {
      observer.next(store.getState());
    });

    return unsubscribe;
  });
}
Enter fullscreen mode Exit fullscreen mode

Unsubscribing in RxJS will be done like this:

const subscription = getState$(store);
subscription.unsubscribe();
Enter fullscreen mode Exit fullscreen mode

Final working function:

Here’s a fully working function with comments, that receives the Redux store as an argument, and returns the state as an Observable:

import { Observable } from 'rxjs/Observable';

function getState$(store) {
  return new Observable(function (observer) {
    // emit the current state as first value:
    observer.next(store.getState());

    const unsubscribe = store.subscribe(function () {
      // emit on every new state changes
      observer.next(store.getState());
    });

    // let's return the function that will be called
    // when the Observable is unsubscribed
    return unsubscribe;
  });
}
Enter fullscreen mode Exit fullscreen mode

Short cut with Observable.from()

While we did everything manually in this post to go through the process of creating an Observable out of Redux store, you can also just use Observable.from():

import { from } from 'rxjs/observable/from';

const state$ = from(store);
Enter fullscreen mode Exit fullscreen mode

Applying RxJS operators on your Redux state

Operators in RxJS will allow to process your state further with ease.

Mapping:

You could get only the counter value (integer) out of your state as a stream:

import { map } from 'rxjs/operators/map';

const state$ = getState$(store);

const counter$ = state$.pipe(
  map(state => state.value)
);
Enter fullscreen mode Exit fullscreen mode

The pipe method is introduced since RxJS v5.5, and you can read further about pipeable (previously called "lettable") operators here.

You can then subscribe to counter$ only:

counter$.subscribe(n => console.log(n));
Enter fullscreen mode Exit fullscreen mode

Filtering:

You can decide which values are emitted with filter. Let’s say, you only want to emit values, if the numbers are even:

import { map } from 'rxjs/operators/map';
import { filter } from 'rxjs/operators/filter';

const state$ = getState$(store);

const evenCounter$ = state$.pipe(
  map(state => state.value),
  filter(n => n % 2 === 0)
);
Enter fullscreen mode Exit fullscreen mode

You can learn about more operators in their documentation here.

Closing thoughts

Redux is awesome, and has helped a big chunk of the JavaScript community to think in a functional way. RxJS is great, and is continuously evolving and helping developers embrace reactive programming with ease.

The two make a great pair. And hope you can benefit from both!

You may also want to check out redux-observable, which has a concept of “Epics”, which allows you to access actions as a stream.

This post was fully focused on state as a stream.

Top comments (1)

Collapse
 
universegalaxy1112 profile image
universegalaxy1112

How can we get it done with typescript?
Especially in the following part.
import { from } from 'rxjs/observable/from';

const state$ = from(store);