Introduction to React and Flux

January 27, 2015

I bet you have heard about React, but what about Flux?

Single page applications (SPA) have became the standard for building of modern web applications. The main reason for this is the smooth user experience, which is achieved by employing Javascript frameworks and libraries when communicating with the backend server. Frameworks and libraries such as Angular, Ember, jQuery, Durandal, Backbone, React, Meteor, Grunt, Bower, NPM, Gulp, Broccoli, Karma, Jasmine, Mocha … WAIT WHAT? There are too many technologies to navigate effectively and frameworks are becoming obsolete even before becoming the standard (I am looking at you Angular 1.3, hmm 1.4 but still…). Angular is nice and productive to work with, but recently announced Angular 2.0 is so different from Angular 1.3 (1.4) that it made many developers consider other possibilities. There are popular jokes that the the only thing Angular 2.0 shares with its former incarnation is the name 🙂

The React solution

React is an open source (github) Javascript library maintained (and also internally used) by Facebook. In contrast to Angular, which is a full-fledged MVC framework, React provides functionality only for the V part of the problem. Because of that, developers can’t use React as a sole library for developing a front end without need to invent their own infrastructure or use some other framework such as Backbone (React + Backbone TodoMVC example).

React overview

Example of VERY simple React component (taken from official github)

var Hello = React.createClass({displayName: 'Hello',
    render: function() {
        return React.createElement("div", null, "Hello ", this.props.name);
    }
});
 
React.render(
    React.createElement(Hello, {name: "World"}), 
    document.body
);

There are things like react-router, which enables you to create component like routing rules so that you can activate React component for particular route, but what about state and interaction with backend? When developing an Angular app you most likely use ui-router to define and transit between application states and resolve data from server. User then interacts with UI of the current state (controller methods), which may call services up to the server REST endpoints and then receive data to update the UI. React itself will be of little help in such use cases.

Let’s make it flow by introducing Flux

Flux is the application architecture that Facebook uses for building client-side web applications. It complements React’s composable view components by utilizing an unidirectional data flow. It’s more of a pattern rather than a formal framework, and you can start using Flux immediately without a lot of new code – Wikipedia

Facebook’s reasons for developing React and Flux are described in this video: It is important to notice that it is an architecture and not a framework. Facebook uses the Flux architecture internally but as of January 2015, they only open sourced a small example of how Flux can be implemented, which is not what they use in production. Implementation of Flux should provide application with infrastructure that is not provided by React itself.

Architecture overview

architecture flux

Main concepts

Let us consider a simple scenario of TodoMVC application example they provided on github – user completes a Todo item by clicking on its check icon.

  1. Clicking on the check icon invokes onChange event handler in React component.
    // snippet from TodoItem.react.js simplified for brevity
     
    render: function() {    
        return (
            React.createElement("input", {
                className: "toggle", 
                type: "checkbox", 
                checked: todo.complete, 
                onChange: this._onToggleComplete}
            ), 
        );
    },
     
    _onToggleComplete: function() {
        TodoActions.toggleComplete(this.props.todo);
    }
    
  2. React component contains reference to TodoActions. Appropriate action is called and all relevant data is passed to the function.
    // snippet from TodoActions.js simplified for brevity
     
    var TodoActions = {
        toggleComplete: function(todo) {
            var id = todo.id;
            AppDispatcher.dispatch({
                actionType: TodoConstants.TODO_COMPLETE,
                id: id
            });
        }
    };
    
  3. TodoActions contains reference to AppDispatcher which notifies all stores which are registered in the Dispatcher about new action, dispatcher is just instantiated because its implementation is provided by Facebook’s Flux.
    // AppDispatcher.js
    var Dispatcher = require('flux').Dispatcher;
    module.exports = new Dispatcher();
    
  4. Store contains reference to AppDispatcher and registers a callback for handling dispatched actions (store also decides if it should perform anything based on the action type). In our case store handles TODO_COMPLETE action by updating todo item and emitting change event.
    // snippet from TodoStore.js simplified for brevity
     
    var assign = require('object-assign');
    var CHANGE_EVENT = 'change';
     
    function update(id, updates) {
      _todos[id] = assign({}, _todos[id], updates);
    }
     
    var TodoStore = assign({}, EventEmitter.prototype, {
     
      getAll: function() {
        return _todos;
      },
     
      emitChange: function() {
        this.emit(CHANGE_EVENT);
      },
     
      /**
       * @param {function} callback
       */
      addChangeListener: function(callback) {
        this.on(CHANGE_EVENT, callback);
      }
     
      // ...
    });
     
    AppDispatcher.register(function(action) {
      var text;
     
      switch(action.actionType) {
        case TodoConstants.TODO_COMPLETE:
          update(action.id, {complete: true});
          TodoStore.emitChange();
          break;
     
        // ...
     
        default:
          // no op
      }
    });
    
  5. To close the unidirectional data flow loop we need to get updated data back to the React components. TodoApp component registed change listener for the TodoStore after component addition to DOM (componentDidMount function). When notified, component executes store’s method to fetch updated data.
    // snippet from TodoApp.react.js simplified for brevity
     
    function getTodoState() {
      return {
        allTodos: TodoStore.getAll()
      };
    }
     
    var TodoApp = React.createClass({
     
     
      componentDidMount: function() {
        TodoStore.addChangeListener(this._onChange);
      },
     
      // ...
      _onChange: function() {
        this.setState(getTodoState());
      }
     
    });
    

overview_flux So far it looks nice. Unidirectional data flow reduces complexity and system is easier to reason about. View layer just renders current state of stores, which are the state holders. Mutation to the state is only performed by calling explicitly defined actions (which are handled by stores).

Where is the catch?

There are multiple problems with React + Flux:

  1. No official architecture implementation is provided – Facebook’s implementation is just simple and a small example
  2. A lot of small new competing implementations such as Reflux, Baracks, Fluxy – check the article with comparison of advantages and disadvantages of these implementations
  3. No clear unified way on how to deal with async actions
  4. No clear way of how to resolve dependency between stores (example implementation: Dispatcher contains waitFor method but that’s a bit clunky and based on some blogs it’s not even working correctly under some circumstances)

Conclusion (tl;dr)

React is a performant, view focused, component based library. In itself, it is not sufficient to build a whole SPA. There are many approaches on how to use React with other frameworks (Backbone and even Angular). The preferred way of using React (championed by Facebook) is to follow the Flux architecture. There isn’t currently available mature open source implementation of the Flux architecture, which would provide ecosystem of features that are needed to effectively develop SPA as of January 2015. Because of these reasons, React isn’t a sensible choice when developing standard web applications. In special cases it can be employed as a part of an Angular application to handle a high performance load when displaying big data sets although Angular (1.3) provided possibility to use one time databinding (:: expression) which can be employed in those situations.

About the author: Tomas Herich
Comments
Join us