React Flux

written by devangelist on February 8, 2015 in Frameworks and Javascript and React with no comments

Flux is an architecture for client-side web applications used by Facebook. Hence it is not a framework or a library that has to be implemented, but more of an addition of an unidirectional data flow to the react view components.
Flux applications consist of several components: Actions, dispatchers, stores and views (the actual React components).

React Flux Architecture - unidirectional data flow

  • Actions: helper methods that are used to transport data to the dispatcher. Example: updateText(todold, newText)
  • Dispatcher: Receives actions and broadcasts the payload to the registered callbacks (stores).
  • Stores: Contain application state and logic and have registered callbacks for the dispatcher.
  • Controller Views: React components that call states from the stores and pass them on to child-components as props.

The difference to classic patterns like MVC (Model view controller) is the data flow. If a user interacts with the React View, it sends an action through a central dispatcher on to the different stores, which in turn contain the state and logic. The stores then refresh all affected views.

The view layer is not allowed to directly change the state – it has to send fire and forget commands to the dispatcher. This way views have no other responsibility besides rendering the current state of the application.

A flux Facebook example

A good example from Facebook explains the sense of this approach: If someone sends a new message, a red bubble icon appears in the menu bar, signaling a new unread message. At the same time the messages also appears in the chat-window. If the message is read, the red notification icon disappears.

To reproduce this scenario in a MVC application, the messages-model and the unread-messages model would need to be refreshed. These dependencies as well as such cascading updates are no exception in large web applications and often lead to complex data flows and unpredictable results.

Using flux on the other hand the view would generate a new action and send this action through the central dispatcher. The registered stores can then decide what they want to do with the update (i.e. hide the notification icon if all messages have been read). No component outside of the stores knows how it manages the data for its domain. Which in the end leads to a better “Separation of concerns”.

A single dispatcher

Within a flux application the dispatcher is the central hub, which manages the complete flow of data. It doesn’t use a great logic, but receives actions from different sources (user interaction in the views, Server/API, …) and distributes them to individual stores who previously registered via a callback.

Flux Dispatcher - Store Callbacks

The dispatcher can manage the dependencies between the individual stores. For example by executing the registered callbacks in a given order and awaiting callbacks before continuing.

var Dispatcher = require('flux').Dispatcher;
var AppDispatcher = new Dispatcher();

AppDispatcher.handleViewAction = function(action) {
  this.dispatch({
    source: 'VIEW_ACTION',
    action: action
  });
}

module.exports = AppDispatcher;

The handleViewAction methods call the dispatch method, which passes on the action-payload to all registered callbacks. The view action statement is meaningful in order to differentiate between view, server and API actions.

Flux Stores

The task of flux stores is similar to that of a classical MVC model. However a store doesn’t only manage one model like it is the case for an ORM model, but the state of several objects. But it is not supposed to be a collection like it is known from Backbone, but an application state for a certain domain within the application.

As previously explained, a store registers itself with the dispatcher via a callback. The callback receives the action as a parameter. Within this callback a switch-statement on the actionType is used to prompt which internal methods are supposed to be executed:

var AppDispatcher = require('../dispatcher/AppDispatcher');
var EventEmitter = require('events').EventEmitter;
var TodoConstants = require('../constants/TodoConstants');
var assign = require('object-assign');
var _todos = {};

var TodoStore = assign({}, EventEmitter.prototype, {

  getAll: function() {
    return _todos;
  },

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

  addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
  },

  removeChangeListener: function(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  }
}

AppDispatcher.register(function(action) {
  var text;

  switch(action.actionType) {
    case TodoConstants.LOAD_TODOS:
      // Call internal method like loadTodos...
      _todos = action.data;
      TodoStore.emitChange();
      break;

    // ...
  }
});

module.exports = TodoStore;

If a store is refreshed, a status notification is sent out to the views with the status event, so that these can be refreshed with the new state.

In the example above it is important that the store of NodeJS’s EventEmitter is being extended so that the stores can listen on events or that they are able to send events. At the same time the view components can listen on these events and refresh or re-render if any changes occurred.

Actions and Action Creators

Action creators are semantic helper methods that send an action to the dispatcher. If the text of a ToDo item needs to be changed for example, an action updateText(todoId, newText) can be created.

var AppDispatcher = require('../dispatcher/AppDispatcher');
var TodoConstants = require('../constants/TodoConstants');
var TodoActions = {
  updateText: function(id, text) {
    AppDispatcher.dispatch({
      actionType: TodoConstants.TODO_UPDATE_TEXT,
      id: id,
      text: text
    });
  }
};
module.exports = TodoActions;

Action creators are usually being executed in the views’ event handlers in order to better react to user interactions. These can also be helpful in the case that new data is sent from the server.

Dependencies with waitFor

If a part of the application is depending on another part, the waitFor methods in the dispatcher module can be used. However the return value of the registration method in the store must be set first:

PrependedTextStore.dispatchToken = Dispatcher.register(function (payload) {
  // ...
});

Afterwards the waitFor method (with the dispatchTokes statement) can be used in the ToDo store:

case 'TODO_CREATE':
  Dispatcher.waitFor([
    PrependedTextStore.dispatchToken,
    YetAnotherStore.dispatchToken
  ]);

Conclusion

In conclusion the following diagram can sum up the data flow in flux:

Facebook React/Flux Architecture

Facbeook’s flux implementation is not the only one: By now there are many more like Fluxxor, RefluxJS or Yahoo’s fluxible-app approach.

Once the first application in flux has been developed, React without flux feels like DOM manipulations in JavaScript without jQuery a few years ago. ;-)

React Flux: 1 Star2 Stars3 Stars4 Stars5 Stars 5.00 out of 5, 2 votes.
Loading ... Loading ...

About the Author

Roberto Bez is a passionate Webdeveloper and TechLead at HolidayCheck. For Roberto development is not only work or a job, but a great motivation and a new challange every day. Everything new and geeky, from new web-technologies to all kind of databases – he tries to introduce it in the daily development.