Flux from Scratch

I’ve been getting into React.js a lot lately and it’s been a very refreshing and enlightening way to do front-end development. During my initial experiments, since my last primary stack was Backbone.js, it was natural for me to simply replace Backbone’s View with React components. This left all of the data handling bits and pieces to Backbone Model and Collection.

At first, I was ok with this. As my project went on I started to see a lot of the mess that I’m used to in Backbone apps (written by my past-self and others alike) creep back in. After a while I decided that this new fangled ‘Flux’ thing might be worth a try.

Fast-forward 6 months or so and I’m happily using Flux (with Immutable.js) and React as my primary front-end stack.

During this time one thing that I’ve seen over and over is people confused about what Flux is exactly, how to use it, what benefits it has, and what’s up with ‘action creators’!?

Learn by Doing

I struggled with it at first too. It’s a very ‘functional’ solution to what we have traditionally handled with an “I don’t know just like fire events and stuff” approach.

One of the best ways I’ve found to understand a new and foreign concept is to implement it myself in a very naive way. This lets you apply the basics of the thing without worrying about all the incidental complexity that comes with a ‘real’ implementation.

Flux is perfect for this because it’s really just a ‘pattern’ and not concrete code in the first place.

So we’ll implement a completely standalone Flux setup from scratch, not using any existing library or framework (even as a reference).

We’ll simply use this basic description:

Flux is a pattern/architecture where data flows in only 1 direction through the system. Changes (view actions/etc) are funneled through a central dispatcher, data stores register handlers for those actions and emit change events, and finally views update in response.

Here’s a handy diagram (based on one from Facebook’s Flux docs):

Flux Unidirectional Data Flow Diagram

The most important/core concept is this unidirectional data flow. The mess in Backbone code (in hindsight) ultimately comes from the adhoc updating all over the place. this.model.set( { x: y } ) could be anywhere. As could this.model.save, or really this.anything.set… how can we be sure? We have to comb the entire codebase to really know where data/changes are coming from.

The Dispatcher

Inarguably, the first thing we’ll need is a dispatcher. Facebook provides one. I do mostly use that in my projects, but we’re trying to understand it here not use off-the-shelf pieces. So let’s whip it up.

The dispatcher at its core is just a list of callbacks and a way to dispatch payloads to those callbacks.

var Dispatcher = function() {
  var _id = 0
  var _callbacks = {}
  return {
    register: function( callback ) {
      _callbacks[_id] = callback
      return _id++
    },
    unregister: function( id ) {
      delete _callbacks[id]
    },
    dispatch: function( payload ) {
      for( var id in _callbacks ) { _callbacks[id](payload) }
    }
  }
}

You’d use it like this:

var dispatcher = new Dispatcher()
var handlerId = dispatcher.register( function( payload ) {
  console.log( "GOT PAYLOAD: " + JSON.stringify(payload) )
} )
dispatcher.dispatch( { test: 1 } ) // outputs payload to console
dispatcher.unregister( handlerId )
dispatcher.dispatch( { test: 2 } ) // does not output to console

Note: For the sake of compatibility, I’m not using ES6/Babel here, although I would definitely do so in my own code.

This is pretty naive, but it works! It has the important features from Facebook’s Flux that we’ll need.

Stores

Stores are where you keep your data. It’s easy to over-complicate this and use something like Backbone.Model and Backbone.Collection, but there are more features we don’t need than features we do. In fact they are anti-features since we explicitly don’t want our views or anything else mucking with the data whenever they feel like it.

Instead we can write a pure-JavaScript store easily that does only what we need.

What are the key functions of a store?

  • It registers a callback with the dispatcher.
  • When it gets a payload, it looks at it to see if it cares (‘is this relevant to me?’)
  • If not, do nothing at all.
  • If so, take the appropriate action and trigger a change event.

That last part is a bit annoying, it sounds like we need an event emitter. Writing one is more than a little yak-shavy, and that is something we must avoid at all costs.

But hold on a second. I used the word ‘trigger’, but maybe we can break it down further. What we need to do is have a way for interested parties (eg. views which want to know when the store has changed) give us a callback to call. Sounds a bit like our dispatcher, doesn’t it? We can just cheat and re-use that!

Here’s a store for a todo list (of course):

var TodoStore = function() {
  var _emitter = new Dispatcher()
  var _todos = {}

  dispatcher.register( function( payload ) {
    switch( payload.type ) {
      case 'CREATE_TODO':
        _todos[payload.todo.id] = payload.todo
        break
      case 'UPDATE_TODO':
        Object.assign( _todos[payload.todo.id], payload.todo )
        break
      case 'REMOVE_TODO':
        delete _todos[payload.todo.id]
        break
      default:
        return // irrelevant payload
    }
    _emitter.dispatch()
  } )

  return {
    getTodos: function() { return _todos },
    addListener: _emitter.register,
    removeListener: _emitter.unregister
  }
}

And again usage is simple:

var todos = new TodoStore()
console.log( todos.getTodos() ) // outputs {}

todos.addListener( function() {
  console.log( "TODOS UPDATED" )
} )

// the id here would come from your server (more on that later)
dispatcher.dispatch( {
  type: 'CREATE_TODO',
  todo: { id: 1, title: "Write 'Flux from Scratch'" }
} ) // outputs TODOS UPDATED

console.log( todos.getTodos()[1].title ) // outputs "Write 'Flux from Scratch'"

We could already use these features to make a simple todo app! But first…

Action Creators (& handling async stuff)

One problem with our setup here is that the store just has an object with a bunch of other objects in it, we actually want to create the todos on a server or save them to Dropbox or whatever. Should we just throw the AJAX bits right there in the switch cases? Or should we do it from the views and only dispatch when we’re done? Neither seems good. Instead we can abstract the creation of the action into Action Creators.

Already we’re seeing the benefits of Flux, by the way. Since every piece is completely independent (and testable in isolation, I might add), we can simply insert another layer in the middle without refactoring the whole thing.

Here’s some todo action creators:

var TodoActions = function() {
  // of course this lacks all error handling for brevity

  var BACKEND = 'https://yourserver.com'
  function parse( r ) { return JSON.parse( r.responseText ) }
  var dispatch = dispatcher.dispatch.bind(dispatcher)

  return {
    createTodo: function( todo ) {
      var r = new XMLHttpRequest()
      r.open( 'POST', BACKEND+'/todos', true )
      r.onload = function() { dispatch( { type: 'CREATE_TODO', todo: parse(r) } ) }
      r.send( JSON.stringify(todo) )
    },

    // update and remove are similar except with
    // PUT/UPDATE_TODO and DELETE/REMOVE_TODO
  }
}

By now this is feeling pretty straight-forward I hope.

var todoActions = new TodoActions()

// when user fills in a form field
todoActions.createTodo( { title: 'something', completed: false } )

// a user completes a todo
todoActions.updateTodo( someId, { completed: true } )

// a user removes a todo
todoActions.removeTodo( someId )

Rendering

We now have a simple Flux system built. The only way to modify the data in the store is to dispatch an action (preferably via an action creator). The store does what the action describes and lets everyone who cares know about it. Those pieces then do whatever the right thing for them to update the interface is.

In the case of React.js, you’ll mainly want to just re-render the entire app from the very top level. You can do this via React.render or via setState.

But you don’t have to use React at all. As we have just seen, Flux is a pattern that can apply to any sort of front-end (or even back-end!) setup you’re using.

Demo

So as a final example, I wrote up a little jQuery soup todo app using all of the above. There are some differences:

  • removed the bit about a backend server and went with a simple local _nextId type system for brevity and simplicity.
  • added an action SEED_TODOS for an initial data load.
  • added updating and removing todos
  • a bit of rough styling.

Overall the gist is the same as the above though.

See the Pen Flux from Scratch by Ryan Funduk (@rfunduk) on CodePen.

I think this is a great way to learn something. I wouldn’t use this rough code in a production app - there’s really no need with so many awesome Flux implementations out there - but I certainly had a deeper understanding of the concepts after doing it by hand like this.