François Vaux

software developer boardgames lover

A simpler alternative to Flux & Redux

February 29th, 2016

Why?

While I like Flux and love Redux, I always found them to be a little bit complicated to grasp.

Apart from being radical new approaches to how we design and develop Web applications, they also come with their lot of boilerplate, which I often find unnecessary and cumbersome.

The Flux Architecture Overview is a difficult read, especially if you're not accustomed to these architecture patterns.

On the other hand, the Redux documentation is quite meaty but introduces a lot of concepts which can also put off beginners.

Proposal

I wanted to keep the Three Principles of Redux:

But I wanted to do this while eliminating the parts I didn't like, and most of the boilerplate.

After some fiddling and discussions on the Putain de Code chatroom, I came up with my own implementation and Uhsac found a name for it: Madux ☺. Although the name was nice, I found another one: elfi which stands for Elegant & Lightweight Flux Implementation.

Here is the implementation of a store in Elfi:

function createStore(initialState) {
  if (!initialState) {
    throw new Error("Missing initial state in store creation")
  }
  let state = initialState
  const subscribers = new Set()

  return {
    getState() {
      return state
    },

    dispatch(action, ...args) {
      if (typeof action !== "function") {
        throw new Error("action must be a function")
      }

      const oldState = state
      state = action(state, ...args)

      if (state === oldState) {
        return
      }

      subscribers.forEach((subscriber) => subscriber(state, oldState))
    },

    subscribe(subscriber) {
      subscribers.add(subscriber)

      return function unsubscribe() {
        if (subscribers.has(subscriber)) {
          subscribers.delete(subscriber)
        }
      }
    },
  }
}

As you can see the code is quite short and expressive. Its concepts are simple to grasp as well:

Below is a simple app built with Elfi, Deku and Immutable.

/** @jsx element */

import {element, createApp} from 'deku'
import Immutable from 'immutable'
import {createStore} from 'elfi'

const initialState = Immutable.fromJS({
  isOn: false
})

const actions = {
  toggleOnOff (state) {
    return state.set('isOn', !state.get('isOn'))
  }
}

const App = {
  render ({context, dispatch}) {
    const isOn = context.get('isOn')
    return (
      <div>
        <button onClick={() => dispatch(actions.toggleOnOff)}>
          Click me!
        </button>
        <span>
          {isOn ? 'On.' : 'Off.'}
        </span>
      </div>
    )
  }
}

const store = createStore(initialState)
const render = createApp(document.getElementById('app'), store.dispatch)
store.subscribe((state) => render(<App />, state))
store.dispatch((s) => s)

What's next?

I still haven't found how to elegantly deal with async actions, maybe by checking the return type of an action and act differently when it's a promise.

Using two actions like in Redux works (one to the request, one for the response) but it does bring some bloat to the code.

I'm also thinking about a way to implement middleware-like functionality, like in Redux.