AddThis Blog

The Case for a Stricter Flux

With companies like Yahoo, AirBnB, Atlassian, and Github embracing React.js, I think it’s safe to say that Facebook has set a new standard in front-end development with this remarkable library. What drew me to React, at first, was its genius approach to rendering. What has kept me using it are things like propTypes, invariant, and the belief that it’s a library’s responsibility to yell at a developer when they are doing something wrong.

While React does provide a really swell way to render views, it intentionally does not provide a framework for control and management of data. Enter Flux: a simple paradigm that creates easy-to follow, easy-to-debug flow of information through your application.

It’s important to know that Flux is not a framework. Flux is more of a design pattern than a piece of code. Flux only works when developers hold themselves to certain conventions, which are sometimes not so obvious. When a developer makes the inevitable mistake, we only sometimes see the error propagate its way into an invariant thrown by React or the Flux dispatcher. The rest of the time, these mistakes manifest themselves in strange and unpredictable ways.

Consider the following code from Facebook’s own page on using Flux:


var _todos = {};
/*...*/
var TodoStore = merge(EventEmitter.prototype, {

  /**
   * Get the entire collection of TODOs.
   * @return {object}
   */
  getAll: function() {
    return _todos;
  },

  /*...*/
}

It might not be obvious how dangerous the code above is. Clearly, an attempt to make _todos private to the store has been made by the developer, yet the store exposes the object through a public function. All a view needs to do to completely break the Flux cycle is delete TodoStore.getAll().id. You’ve probably run in to this if you’re developing an application with Flux and not using an Immutable library—and you probably also know it’s now time to bring out the defensive cloning:


var TodoStore = merge(EventEmitter.prototype, {
  /*...*/
  getAll: function() {
    return $.extend(true, {}, _todos);
  },
  /*...*/
}

But that doesn’t solve every problem. Imagine your view receives the result of a call to getAll as a prop, and then passes this prop along to the various children that need it:


<Todo data={this.props.todos[0]}>
    <TodoLabel id=this.props.todos[0].id>
        {this.props.todos[0].description}
    </TodoLabel>
</Todo>

What happens when a developer makes the mistake of mutating one of their props? It’s certainly convention to not do this, but it’s easy enough to flub.


var todo = this.props.todo;
/*...*/
function saveTodo(todo) {
    todo.id = generateUUID();
    //description -> text for legacy support
    todo.text = todo.description;
    delete todo.description;
}

Clearly we are going to have some problems, now that this mutable, cloned copy is being shared by multiple components. This kind of thing is a nightmare to debug. Why wasn’t it stopped earlier? Why do our stores even allow us to use anything other than immutable data?

At AddThis, we’re using the hand-made Flux components provided by Facebook, but all the devs here have felt the pains I’ve enumerated above. These are the kinds of pains a framework should solve. There are a few Flux frameworks in the wild right now, but they all seem focused on reducing boilerplate (a noble goal, but should it be the only one?). Until a Flux framework can give us the rigidity that React does for views, we won’t invest the time to migrate to one.