Tutorial

Getting To Know Flux, the React.js Architecture

Draft updated on Invalid Date
Default avatar

By Ken Wheeler

Getting To Know Flux, the React.js Architecture

This tutorial is out of date and no longer maintained.

Introduction

Welcome to the third installment of the Learning React series. Today we will be learning about how Facebook’s Flux Architecture works, and how to use it in your own projects!

If you haven’t already, I strongly recommend you check out the first two installments in this series, Getting Started & Concepts and Building a Real-Time Twitter Stream with Node and React. They aren’t a hard prerequisite, but will certainly help you understand this article if you don’t already have familiarity with React.js.

What is Flux?

Flux is an architecture that Facebook uses internally when working with React. It is not a framework or a library. It is simply a new kind of architecture that complements React and the concept of Unidirectional Data Flow.

That said, Facebook does provide a repo that includes a Dispatcher library. The dispatcher is a sort of global pub/sub handler that broadcasts payloads to registered callbacks.

A typical Flux architecture will leverage this Dispatcher library, along with NodeJS’s EventEmitter module in order to set up an event system that helps manage an application’s state.

Flux is probably better explained by explaining its individual components:

  • Actions - Helper methods that facilitate passing data to the Dispatcher
  • Dispatcher - Receives actions and broadcasts payloads to registered callbacks
  • Stores - Containers for application state & logic that have callbacks registered to the dispatcher
  • Controller Views - React Components that grab the state from Stores and pass it down via props to child components.

How does the API relate to this?

When you are working with data that is coming from (or going to) the outside, I’ve found that using Actions to introduce the data into the Flux Flow, and subsequently Stores, is the most painless way to go about it.

The Dispatcher

So what’s this Dispatcher all about?

The Dispatcher is basically the manager of this entire process. It is the central hub for your application. The dispatcher receives actions and dispatches the actions and data to registered callbacks.

So it’s essentially pub/sub?

Not exactly. The dispatcher broadcasts the payload to ALL of its registered callbacks, and includes functionality that allows you to invoke the callbacks in a specific order, even waiting for updates before proceeding. There is only ever one dispatcher, and it acts as the central hub within your application.

Check out what one looks like below:

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

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

    module.exports = AppDispatcher;

In the above example, we create an instance of our Dispatcher and create a handleViewAction method. This abstraction is helpful if you are looking to distinguish between view triggered actions v.s. server/API triggered actions.

Our method calls the dispatch method, which will broadcast the action payload to all of its registered callbacks. This action can then be acted upon within Stores, and will result in a state update.

Dependencies

One of the coolest parts of the provided Dispatcher module is the ability to define dependencies and marshall the callbacks on our Stores. So if one part of your application is dependent upon another part being updated first, in order to render properly, the Dispatcher’s waitFor method will be mighty useful.

In order to utilize this feature, we need to store the return value of the Dispatcher’s registration method on our Store as dispatcherIndex, as shown below:

    ShoeStore.dispatcherIndex = AppDispatcher.register(function(payload) {

    });

Then in our Store, when handling a dispatched action, we can use the Dispatcher’s waitFor method to ensure our ShoeStore has been updated:

    case 'BUY_SHOES':
      AppDispatcher.waitFor([
        ShoeStore.dispatcherIndex
      ], function() {
        CheckoutStore.purchaseShoes(ShoeStore.getSelectedShoes());
      });
      break;

Stores

In Flux, Stores manage application state for a particular domain within your application. From a high level, this basically means that per app section, stores manage the data, data retrieval methods, and dispatcher callbacks.

Lets take a look at a basic Store:

    var AppDispatcher = require('../dispatcher/AppDispatcher');
    var ShoeConstants = require('../constants/ShoeConstants');
    var EventEmitter = require('events').EventEmitter;
    var merge = require('react/lib/merge');

    // Internal object of shoes
    var _shoes = {};

    // Method to load shoes from action data
    function loadShoes(data) {
      _shoes = data.shoes;
    }

    // Merge our store with Node's Event Emitter
    var ShoeStore = merge(EventEmitter.prototype, {

      // Returns all shoes
      getShoes: function() {
        return _shoes;
      },

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

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

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

    });

    // Register dispatcher callback
    AppDispatcher.register(function(payload) {
      var action = payload.action;
      var text;
      // Define what to do for certain actions
      switch(action.actionType) {
        case ShoeConstants.LOAD_SHOES:
          // Call internal method based upon dispatched action
          loadShoes(action.data);
          break;

        default:
          return true;
      }

      // If action was acted upon, emit change event
      ShoeStore.emitChange();

      return true;

    });

    module.exports = ShoeStore;

The most important thing we did above is to extend our store with NodeJS’s EventEmitter. This allows our stores to listen for and broadcast events. This allows our Views and Components to update based upon those events. Because our Controller View listens to our Stores, leveraging this to emit change events will let our Controller View know that our application state has changed, and it’s time to retrieve the state to keep things fresh.

We also registered a callback with our AppDispatcher using its register method. This means that our Store is now listening to AppDispatcher broadcasts. Our switch statement determines whether, for a given broadcast, if there are any relevant actions to take. If a relevant action is taken, a change event is emitted, and views that are listening for this event update their states.

Our public method getShoes is used by our Controller View to retrieve all of the shoes in our _shoes object and use that data in our components state. While this is a simple example, complicated logic can be put here instead of our views and helps keep things tidy.

Action Creators & Actions

Action Creators are collections of methods that are called within views (or anywhere else for that matter) to send actions to the Dispatcher. Actions are the actual payloads that are delivered via the dispatcher.

The way Facebook uses them, action type constants are used to define what action should take place and are sent along with action data. Inside of registered callbacks, these actions can now be handled according to their action type, and methods can be called with action data as the arguments.

Lets check out a constants definition:

    var keyMirror = require('react/lib/keyMirror');

    module.exports = keyMirror({
      LOAD_SHOES: null
    });

Above we use React’s keyMirror library to mirror our keys so that our value matches our key definition. Just by looking at this file, we can tell that our app loads shoes. The use of constants helps keep things organized, and helps give a high-level view of what the app actually does.

Now lets take a look at the corresponding Action Creator definition:

    var AppDispatcher = require('../dispatcher/AppDispatcher');
    var ShoeStoreConstants = require('../constants/ShoeStoreConstants');

    var ShoeStoreActions = {

      loadShoes: function(data) {
        AppDispatcher.handleAction({
          actionType: ShoeStoreConstants.LOAD_SHOES,
          data: data
        })
      }

    };

    module.exports = ShoeStoreActions;

In our example above, we created a method on our ShoeStoreActions object that calls our dispatcher with the data we provided. We can now import this actions file into our view or API, and call ShoeStoreActions.loadShoes(ourData) to send our payload to the Dispatcher, which will broadcast it. Then the ShoeStore will “hear” that event and call a method that loads up some shoes!

Controller Views

Controller views are really just React components that listen to change events and retrieve Application state from Stores. They then pass that data down to their child components via props.

Here is what this looks like:

    /** @jsx React.DOM */

    var React = require('react');
    var ShoesStore = require('../stores/ShoeStore');

    // Method to retrieve application state from store
    function getAppState() {
      return {
        shoes: ShoeStore.getShoes()
      };
    }

    // Create our component class
    var ShoeStoreApp = React.createClass({

      // Use getAppState method to set initial state
      getInitialState: function() {
        return getAppState();
      },

      // Listen for changes
      componentDidMount: function() {
        ShoeStore.addChangeListener(this._onChange);
      },

      // Unbind change listener
      componentWillUnmount: function() {
        ShoesStore.removeChangeListener(this._onChange);
      },

      render: function() {
        return (
          <ShoeStore shoes={this.state.shoes} />
        );
      },

      // Update view state when change event is received
      _onChange: function() {
        this.setState(getAppState());
      }

    });

    module.exports = ShoeStoreApp;

In the example above, we listen for change events using addChangeListener, and update our application state when the event is received.

Our application state data is held in our Stores, so we use the public methods on the Stores to retrieve that data and then set our application state.

Putting It All Together

Now that we have gone through each individual part of the Flux architecture, we should have a much better idea of how this architecture actually works. Remember our graphical representation of this process from before?

Conclusion

After reading this article, I hope that if you didn’t “get” Facebook’s Flux Architecture before, that now you can say you do. It wasn’t until building something with it that I understood how complimentary to React.js it actually is.

After you use Flux the first time, writing React without Flux feels like DOM traversal without jQuery. You can absolutely do it, but it feels less elegant and structured.

If you want to use the Flux architecture, but you don’t want to use React, check out Delorean, a Flux framework that you can use with Ractive.js or Flight. Another library worth looking at is Fluxxor, which takes a different approach to Flux architecture and provides a tighter coupling of Flux’s components into a central Flux instance.

Again, I believe that to truly understand Flux, you actually have to use it, so stay tuned for the 4th and final installment of Learning React where we will be building a shopping cart with React.js and Flux Architecture!

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Ken Wheeler

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel