Data Science and Databases
9 minute read

Simple Data Flow in React Apps Using Flux and Backbone: A Tutorial with Examples

Alex is an entrepreneur and Wharton grad who recently closed a startup and is contracting while he travels.

React.js is a fantastic library. Sometimes it seems like the best thing since sliced Python. React is only one part of a front-end application stack, however. It doesn’t have much to offer when it comes to managing data and state.

Facebook, the makers of React, have offered some guidance there in the form of Flux. Flux is an “Application Architecture” (not a framework) built around one-way data flow using React Views, an Action Dispatcher, and Stores. The Flux pattern solves some major problems by embodying important principles of event control, which make React applications much easier to reason about, develop, and maintain.

Here, I’ll introduce basic Flux examples of control flow, discuss what’s missing for Stores, and how to use Backbone Models and Collections to fill the gap in a “Flux-compliant” way.

(Note: I use CoffeeScript in my examples for convenience and brevity. Non-CoffeeScript developers should be able to follow along, and can treat the examples as pseudocode.)

Introduction to Facebook’s Flux

Backbone is an excellent and well-vetted little library that includes Views, Models, Collections, and Routes. It’s a de facto standard library for structured front-end applications, and it’s been paired with React apps since the latter was introduced in 2013. Most examples of React outside of Facebook.com so far have have included mentions of Backbone being used in tandem.

Unfortunately, leaning on Backbone alone to handle the entire application flow outside of React’s Views presents unfortunate complications. When I first began working on React-Backbone application code, the “complex event chains” that I had read about didn’t take long to rear their hydra-like heads. Sending events from the UI to the Models, and then from one Model to another and then back again, makes it hard to keep track of who was changing who, in what order, and why.

This Flux tutorial will demonstrats how the Flux pattern handles these problems with impressive ease and simplicity.

An Overview

Flux’s slogan is “unidirectional data flow”. Here’s a handy diagram from the Flux docs showing what that flow looks like:

Facebook Flux uses an “unidirectional data flow” model that varies a little when paired with React and Backbone.

The important bit is that stuff flows from React --> Dispatcher --> Stores --> React.

Let’s look at what each of the main components are and how they connect:

The docs also offer this important caveat:

Flux is more of a pattern than a framework, and does not have any hard dependencies. However, we often use EventEmitter as a basis for Stores and React for our Views. The one piece of Flux not readily available elsewhere is the Dispatcher. This module is available here to complete your Flux toolbox.

So Flux has three components:

  1. Views (React = require('react'))
  2. Dispatcher (Dispatcher = require('flux').Dispatcher)
  3. Stores (EventEmitter = require('events').EventEmitter)
    • (or, as we’ll soon see, Backbone = require('backbone'))

The Views

I won’t describe React here, since so much has been written about it, other than to say that I vastly prefer it to Angular. I almost never feel confused when writing React code, unlike Angular, but of course, opinions will vary.

The Dispatcher

The Flux Dispatcher is a single place where all events that modify your Stores are handled. To use it, you have each Store register a single callback to handle all events. Then, whenever you want to modify a Store, you dispatch an event.

Like React, the Dispatcher strikes me as a good idea, implemented well. As an example, an app that allows the user to add items to a to-do list might include the following:

# in TodoDispatcher.coffee
Dispatcher = require("flux").Dispatcher

TodoDispatcher = new Dispatcher() # That's all it takes!.

module.exports = TodoDispatcher    
# in TodoStore.coffee
TodoDispatcher = require("./TodoDispatcher")

TodoStore = {items: []}

TodoStore.dispatchCallback = (payload) ->
  switch payload.actionType
    when "add-item"
      TodoStore.items.push payload.item
    when "delete-last-item"
      TodoStore.items.pop()

TodoStore.dispatchToken = TodoDispatcher.registerCallback(TodoStore.dispatchCallback)

module.exports = TodoStore
# in ItemAddComponent.coffee
TodoDispatcher = require("./TodoDispatcher")

ItemAddComponent = React.createClass
  handleAddItem: ->
    # note: you're NOT just pushing directly to the store!
    # (the restriction of moving through the dispatcher
    # makes everything much more modular and maintainable)
    TodoDispatcher.dispatch
      actionType: "add-item"
      item: "hello world"

  render: ->
    React.DOM.button {
      onClick: @handleAddItem
    },
      "Add an Item!"

This makes it really easy to answer two questions:

  1. Q: What are all the events that modify MyStore?
    • A: Just check the cases in the switch statement in MyStore.dispatchCallback.
  2. Q: What are all possible sources of that event?
    • A: Simply search for that actionType.

This is much easier than, for example, looking for MyModel.set and MyModel.save and MyCollection.add etc, where tracking down the answers to these basic questions gets really hard really fast.

The Dispatcher also allows you to have callbacks run sequentially in a simple, synchronous fashion, using waitFor. For example:

# in MessageStore.coffee
MyDispatcher = require("./MyDispatcher")
TodoStore = require("./TodoStore")

MessageStore = {items: []}

MessageStore.dispatchCallback = (payload) ->
  switch payload.actionType
    when "add-item"
      # synchronous event flow!
      MyDispatcher.waitFor [TodoStore.dispatchToken]

      MessageStore.items.push "You added an item! It was: " + payload.item

module.exports = MessageStore

In practice, I was shocked to see how much cleaner my code was when using the Dispatcher to modify my Stores, even without using waitFor.

The Stores

So data flows into Stores through the Dispatcher. Got it. But how does data flow from the Stores to the Views (i.e., React)? As stated in the Flux docs:

[The] view listens for events that are broadcast by the stores that it depends on.

Okay, great. Just like we registered callbacks with our Stores, we register callbacks with our Views (which are React Components). We tell React to re-render whenever a change occurs in the Store which was passed in through its props.

For example:

# in TodoListComponent.coffee
React = require("react")

TodoListComponent = React.createClass
  componentDidMount: ->
    @props.TodoStore.addEventListener "change", =>
      @forceUpdate()
    , @

  componentWillUnmount: ->
    # remove the callback

  render: ->
    # show the items in a list.
    React.DOM.ul {}, @props.TodoStore.items.map (item) ->
      React.DOM.li {}, item

Awesome!

So how do we emit that "change" event? Well, Flux recommends using EventEmitter. From an official example:

var MessageStore = merge(EventEmitter.prototype, {

  emitChange: function() {
    this.emit(CHANGE_EVENT);
  },

  /**
   * @param {function} callback
   */
  addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
  },

  get: function(id) {
    return _messages[id];
  },

  getAll: function() {
    return _messages;
  },
// etc...

Gross! I have to write all that myself, every time I want a simple Store? Which I’m supposed to use every time I have a piece of information I want to display? There has to be a better way!

The Missing Piece

Backbone’s Models and Collections already have everything Flux’s EventEmitter-based Stores seem to be doing.

By telling you to use raw EventEmitter, Flux is recommending that you recreate maybe 50-75% of Backbone’s Models & Collections every time you create a Store. Using EventEmitter for your stores is like using bare Node.js for your server when well-built microframeworks like Express.js or equivalent already exist to take care of all the basics and boilerplate.

Just like Express.js is built on Node.js, Backbone’s Models and Collections are built on EventEmitter. And it has all the stuff you pretty much always need: Backbone emits change events and has query methods, getters and setters and everything. Plus, Backbone’s Jeremy Ashkenas and his army of 230 contributors did a much better job on all of those things than I am likely to be able to do.

As an example for this Backbone tutorial, I converted the MessageStore example from above to a Backbone version.

It’s objectively less code (no need to duplicate work) and is subjectively more clear and concise (for example, this.add(message) instead of _messages[message.id] = message).

So let’s use Backbone for Stores!

The FluxBone Pattern: Flux Stores by Backbone

This tutorial is the basis of an approach I have proudly dubbed FluxBone, the Flux architecture using Backbone for Stores. Here is the basic pattern of a FluxBone architecture:

  1. Stores are instantiated Backbone Models or Collections, which have registered a callback with the Dispatcher. Typically, this means they are singletons.
  2. View components never directly modify Stores (for example, no .set()). Instead, components dispatch Actions to the Dispatcher.
  3. View components query Stores and bind to their events to trigger updates.

This Backbone tutorial is designed to look at the way Backbone and Flux work together in React applications.

Let’s use Backbone and Flux examples to look at each piece of that in turn:

1. Stores are instantiated Backbone Models or Collections, which have registered a callback with the Dispatcher.

# in TodoDispatcher.coffee
Dispatcher = require("flux").Dispatcher

TodoDispatcher = new Dispatcher() # That's all it takes!

module.exports = TodoDispatcher
# in stores/TodoStore.coffee
Backbone = require("backbone")
TodoDispatcher = require("../dispatcher")

TodoItem = Backbone.Model.extend({})

TodoCollection = Backbone.Collection.extend
  model: TodoItem
  url: "/todo"

  # we register a callback with the Dispatcher on init.
  initialize: ->
    @dispatchToken = TodoDispatcher.register(@dispatchCallback)

  dispatchCallback: (payload) =>
    switch payload.actionType
      # remove the Model instance from the Store.
      when "todo-delete"
        @remove payload.todo
      when "todo-add"
        @add payload.todo
      when "todo-update"
        # do stuff...
        @add payload.todo,
          merge: true
      # ... etc


# the Store is an instantiated Collection; a singleton.
TodoStore = new TodoCollection()
module.exports = TodoStore

2. Components never directly modify Stores (for example, no .set()). Instead, components dispatch Actions to the Dispatcher.

# components/TodoComponent.coffee
React = require("react")

TodoListComponent = React.createClass
  handleTodoDelete: ->
    # instead of removing the todo from the TodoStore directly,
    # we use the Dispatcher
    TodoDispatcher.dispatch
      actionType: "todo-delete"
      todo: @props.todoItem
  # ... (see below) ...

module.exports = TodoListComponent

3. Components query Stores and bind to their events to trigger updates.

# components/TodoComponent.coffee
React = require("react")

TodoListComponent = React.createClass
  handleTodoDelete: ->
    # instead of removing the todo from the TodoStore directly,
    # we use the dispatcher. #flux
    TodoDispatcher.dispatch
      actionType: "todo-delete"
      todo: @props.todoItem
  # ...
  componentDidMount: ->
    # the Component binds to the Store's events
    @props.TodoStore.on "add remove reset", =>
      @forceUpdate()
    , @
  componentWillUnmount: ->
    # turn off all events and callbacks that have this context
    @props.TodoStore.off null, null, this
  render: ->
    React.DOM.ul {},
      @props.TodoStore.items.map (todoItem) ->
        # TODO: TodoItemComponent, which would bind to
        # `this.props.todoItem.on('change')`
        TodoItemComponent {
          todoItem: todoItem
        }

module.exports = TodoListComponent

I have applied this Flux and Backbone approach to my own projects, and once I re-architected my React application to use this pattern, almost all the ugly bits disappeared. It was a little miraculous: one by one, the pieces of code that had me gnashing my teeth looking for a better way were replaced by sensible flow. And the smoothness with which Backbone seems to integrate in this pattern is remarkable: I don’t feel like I’m fighting Backbone, Flux, or React in order to fit them together in a single application.

Example Mixin

Writing the this.on(...) and this.off(...) code every time you add a FluxBone Store to a component can get a bit old.

Here’s an example React Mixin that, while extremely naive, would certainly make iterating quickly even easier:

# in FluxBoneMixin.coffee
module.exports = (propName) ->
  componentDidMount: ->
    @props[propName].on "all", =>
      @forceUpdate()
    , @

  componentWillUnmount: ->
    @props[propName].off "all", =>
      @forceUpdate()
    , @
# in HelloComponent.coffee
React = require("react")

UserStore = require("./stores/UserStore")
TodoStore = require("./stores/TodoStore")

FluxBoneMixin = require("./FluxBoneMixin")


MyComponent = React.createClass
  mixins: [
    FluxBoneMixin("UserStore"),
    FluxBoneMixin("TodoStore"),
  ]
  render: ->
    React.DOM.div {},
      "Hello, #{ @props.UserStore.get('name') },
      you have #{ @props.TodoStore.length }
      things to do."

React.renderComponent(
  MyComponent {
    UserStore: UserStore
    TodoStore: TodoStore
  }
  , document.body.querySelector(".main")
)

Syncing with a Web API

In the original Flux diagram, you interact with the Web API through ActionCreators only, which require a response from the server before sending actions to the Dispatcher. That never sat right with me; shouldn’t the Store be the first to know about changes, before the server?

I choose to flip that part of the diagram around: the Stores interact directly with a RESTful CRUD API through Backbone’s sync(). This is wonderfully convenient, at least if you’re working with an actual RESTful CRUD API.

Data integrity is maintained with no problem. When you .set() a new property, the change event triggers a React re-render, optimistically displaying the new data. When you try to .save() it to the server, the request event lets you know to display a loading icon. When things go through, the sync event lets you know to remove the loading icon, or the error event lets you know to turn things red. You can see inspiration here.

There’s also validation (and a corresponding invalid event) for a first layer of defense, and a .fetch() method to pull new information from the server.

For less standard tasks, interacting via ActionCreators may make more sense. I suspect Facebook doesn’t do much “mere CRUD”, in which case it’s not surprising they don’t put Stores first.

Conclusion

The Engineering teams at Facebook have done remarkable work to push the front-end web forward with React, and the introduction of Flux gives a peek into a broader architecture that truly scales: not just in terms of technology, but engineering as well. Clever and careful use of Backbone (per this tutorial’s example) can fill the gaps in Flux, making it amazingly easy for anyone from one-person indie shops to large companies to create and maintain impressive applications.