Redux in a single line of code with RxJS

The brilliance of Redux lies in it's simplicity. Redux is so simple in fact that when thought of as a stream of states, it can be expressed in a single line of RxJS code.

I was working on a side project that manipulated streams of data and it occurred to me that streams might be a simple and flexible way to handle application state. In the process I discovered that with only one line of code I could get the same sort of core functionality provided by Redux using RxJS. This then provides access to a higher and more powerful abstraction and code vocabulary that can be used elsewhere to homogenise other varied asynchronous strategies such as Promises and callbacks.

So I thought I would write a little bit about the potential of RxJS as a replacement of Redux and provide an example of how to use RxJS to provide Redux style functionality.

Redux

Redux is a very successful interpretation of the Flux architecture to manage state in client-side applications. The main concept around redux is that your app should only have a single data store and that data is a reduction of the previous state and the change brought about by a given action. React components are then connected to the data in the store with a set of utility functions and higher order components and as the store's data is changed through actions being fired so the view reacts.

RxJS

From their website: "Rx or Reactive Extensions is a library for composing asynchronous and event-based programs by using observable sequences."

What they mean here is that RxJS is a framework for working with streams of data over time as if they were arrays. So with RxJS instead of thinking of our application as a structure of objects communicating with one another we think of the application as a signal network through which data is sent through. Eventually at the output of the network a view callback will subscribe to a stream which will pass it a state that is rendered functionally. In most cases React would be the best choice for a renderer.

Thinking about programming like this reminds me of programming with Flow Based Programming audio-visual platforms such as MAX/MSP or vvvv.

Redux is baked in to RxJS

The totally cool thing about RxJS is that the core functionality of Redux can be written using merely a single line of code. Following is how you implement the guts of Redux with RxJS Observables.

action$.scan(reducer).subscribe(renderer)  

Yup that is the whole reduction and state management provided to you weighing in to a grand total of around one line of code and best of all there is no magic (assuming you are comfortable with the way streams work).

Breaking it down

So, for those uninitiated with Rx you are probably asking yourself 'What is going on here?'

The stream

Basically action$ is an Observable stream. The $ in the name is merely convention for showing that the variable is a stream. As an aside, personally I am not thrilled about using things like $ for naming conventions but it is prevalent within the RxJS community which is why I have included it here.

The reducer

The reducer is one of Dan Abramov's reducers; a function that takes a state and an action and immutably returns a newState. In a real app this would be composed of a bunch of other reducer functions combined in whatever way you see fit.

The renderer

The renderer is a function that takes a state and renders that state to a React component tree.

Reducing actions to state

Finally and most importantly, the poorly named .scan() method on the observable is the thing that gives us all the power here. This applies an "accumulation function" over our state$ stream and returns each result as more data comes down the stream.

So if you pass an action data object (however you want to define it) to the head of the action$ stream what happens next is that then out pops your resolved state at the other end thanks to your reducer function. The renderer can then render the output state on the view.

Example app

So maybe I was being a little misleading by saying it only takes a single line of code, however the core functionality is packed away so neatly by RxJS I think it is a rather tight implementation of the same idea as Redux.

A small example application using this pattern might look like:

import React from 'react';  
import ReactDOM from 'react-dom';  
import { Subject } from 'rxjs/Subject';

// create our stream as a subject so arbitrary data can be sent on the stream
const action$ = new Subject();

// Initial State
const initState = { name: 'Harry' };

// Redux reducer
const reducer = (state, action) => {  
  switch(action.type) {
    case 'NAME_CHANGED':
      return {
        ...state,
        name: action.payload
      };
    default:
      return state;
  }
}

// Reduxification
const store$ = action$  
    .startWith(initState)
    .scan(reducer);

// Higher order function to send actions to the stream
const actionDispatcher = (func) => (...args) =>  
  action$.next(func(...args));

// Example action function
const changeName = actionDispatcher((payload) => ({  
  type: 'NAME_CHANGED',
  payload
}));

// React view component 
const App = (props) => {  
  const { name } = props;
  return (
    <div>
      <h1>{ name }</h1>
      <button onClick={() => changeName('Harry')} >Harry</button>
      <button onClick={() => changeName('Sally')} >Sally</button>
    </div>
  );
}

// subscribe and render the view
const dom =  document.getElementById('app');  
store$.subscribe((state) =>  
    ReactDOM.render(<App {...state} />, dom));

Async actions

Let's say we want to do something asynchronous like fetch some information from a rest api all we need to do is send an ajax stream in place of our action payload and then use one of the lodash style stream operators, flatMap to squash the results of the asynchronous operation back onto the action$ stream.

import { isObservable } from './utils';

// Action creator
const actionCreator = (func) => (...args) => {  
  const action = func.call(null, ...args);
  action$.next(action);
  if (isObservable(action.payload))
    action$.next(action.payload);
  return action;
};

// method called from button click
const loadUsers = actionCreator(() => {  
  return {
    type: 'USERS_LOADING',
    payload: Observable.ajax('/api/users')
      .map(({response}) => map(response, 'username'))
      .map((users) => ({
        type: 'USERS_LOADED',
        payload: users
      }))
  };
});

// Reducer
export default function reducer(state, action) {  
  switch (action.type) {
    case 'USERS_LOADING':
      return {
        ...state,
        isLoading: true
      };
    case 'USERS_LOADED':
      return {
        ...state,
        isLoading: false,
        users: action.payload,
      };
    //...
  }
}

// rest of code...

// Wrap input to ensure we only have a stream of observables
const ensureObservable = (action) =>  
  isObservable(action)
    ? action
    : Observable.from([action]);

// Using flatMap to squash async streams
const action$  
    .flatMap(wrapActionToObservable)
    .startWith(initState)
    .scan(reducer);

The advantage of swapping the action payload for a stream is so we can send data updates at the start and the end of the async operation

ReactiveX enables better polyglot communication

In the end, the concepts within Redux are simply Functional Reactive Programming and with Rx being the cross language poster boy for FRP it makes sense that one can use Rx to create Redux.

What is interesting here, is that because Rx is available in 15 different programming languages using it to develop Flux style architectures as a technique will translate cross-language so it is theoretically plausible to use a Flux/Redux style pattern for state storage in languages like Ruby, C# or Java.

This is especially useful in my opinion if you are working in a multi service polyglot environment where occasionally your teams need to work on each others projects. You could be working on separate services but would have a shared functional approach to application state.

Don't ditch Redux

Using Redux is useful in terms of having a language around functional state reduction as a pattern. The tools it provides for debugging state are a great add-on.

This technique however, shows at it's core how simple Redux is and how your app framework actually need not be complex to be useful.

You may want to simply provide your own simple state management. The main benefit here is cutting down of magic you need to grok outside of your codebase and limiting your dependency graph.

If you are sure you will never use RxJS in your app and you want a FRP state store then using Redux makes sense, however in the case where the argument for observable streams has already been made or if your polyglot team has cross cutting concerns I think this is a contender for handling app state.

Example app can be found here: https://github.com/ryardley/rxflux

Rudi Yardley

Read more posts by this author.