unModified()

Break stuff. Now.

The Ractive Flux Experiment

Trying state management on a framework that's not React

May 15, 2015

It's been more than a year since I started writing "real JavaScript" - JavaScript that is actually used in a real product rather than "hobby JavaScript". In that one year, there have been a lot of twists and turns with regards to framework of choice. From Ember to React. From highly cohesive to extremely loosely-coupled. Countless trials, and errors. A lot of pizza, beer and sleep were lost in the war. But I have spoken! I have chosen! It's Ractive... with a bit of Flux.

Ractive

I decided to go with Ractive for a bunch of reasons. I needed something that would allow us to build "legos" - small units of reusable, maintainable and self-contained functionality. Then with these small bits and pieces, build entire pages and apps. Ractive fit the bill with it's simple component spec and best of all, encourages you to use plain HTML, CSS and JS. Best of all, no steep learning curve.

<div>
  <input type="text" value="{{firstName}}">
  <input type="text" value="{{lastName}}">
  <span>Hi! {{firstName}} {{lastName}}</span>
  <span>Your initials are: {{initials}}</span>
</div>

<script>
component.exports = {
  computed: {
    initials: function(){
      var fullName = this.get('firstName') + this.get('lastName');
      return fullName.split(' ').map(function(name){
        return name[0];
      }).join('');
    }
  }
};
</script>

Flux

What got me into Flux was when pages started to contain more than one app, usually unrelated to each other but share some data. I once used an event system, but then found myself in a tangle of events whose origins I know not. Flux fit the bill, along with the concept of stores being the single source of truth. I was even more convinced after I found this slide deck by Jeremy Morrell which nailed down how Flux should be written, Fluxxor which had a diagram of how big applications should be structured with Flux, and Reflux which simplified the methodology.

                                          .--------.
    .--------------------------------------| Action |-----------.
    |     .--------.                       '--------'           |
    |  .- | Action | <----------.                               |
    |  |  '--------'            |                               |
    |  |                        |                               |
    v  V                        |                               |
.----------.    .-------.    .-----------.    .--------------.  |
| Dispatch | -> | Store | -> | Component | -> | SubComponent |  |
'----------'    '-------'    '-----------'    '--------------'  |
    |          .-------.    .-----------.    .--------------.  |
    '--------> | Store | -> | Component | -> | SubComponent | -'
                '-------'    '-----------'    '--------------'

Two-way binding

The magic we all love to hate in simple UI frameworks is the two-way binding. It allows a developer a declarative way of building the UI with as little boilerplate as possible. Event and data manipulation gets abstracted, allowing the user to focus on building the app rather than worry about things like compatibility and event quirks. Doing it wrong, though, would lead to chaos. But two-way binding isn't bad. In fact, it's faster than virtual DOM in certain implementation. You just have to use it properly.

Making it work

Now there was a point in time where I was to decide the fate of JS when a rewrite was planned for the product. On one camp, there's the strong hype of React, Immutable, Flux and the uni-directional architecture. On the other end, the stable monoliths like Angular, Ember, and the two-way binding camp. Then I did the unthinkable. I decided to use both.

How? Let this simple diagram explain:

|---- one-way land ------|----------- two-way land -----------------|

                                  ,-> Component C   ,--> Component E
                                  /                 /
Actions -> Stores -> Component A <---> Component B <----> Component F
  ^  ^                            \
  |  |                             `-> Component D <----> Component G
  |  |                                  |                  |
  |  '--observer sees change in input---'                  |
  '------------------------------ save button clicked -----'

I took advantage of 2-way binding for fast UI development. Ractive has a very simple and fast two-way binding model. The data structure of the component is derrived from the template's mustaches, plain and simple. The other half of the recipe was integrating the Flux pattern. With the UI in place, observers were planted in specific components which, when notified of changes, tell the stores to update. Stores validate the data, flush down changes, and the UI either gets invalidated and re-renders, or does nothing if the data is the same.

Optimistic UI

The nice thing about this approach is it lets your UI be "optimistic". That is, changes the user makes in the UI get reflected immediately due to the two-way binding. The observers then tell the stores of the changes, mutate and flush. Now let's say the data was invalidated, and the store flushed corrected data. All the components do is discard the local change for the flushed changes. This approach is similar to the approach by Meteor for their lag compensation feature.

The adventure's not yet over

The approach is simple, yet it's very powerful. But there's a lot of gotchas I have yet to address in the approach. But from the looks of it, it's pretty understandable. I suggest you go out and try it to see for yourself. If you have comments, feel free to drop a tweet, a DM or an email because I don't have a comment section on my blog... yet. :P