Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

A Detailed Study of Flux: The React.js Application Architecture

DZone's Guide to

A Detailed Study of Flux: The React.js Application Architecture

In this post, we take an up close and personal look at the Flux architectural pattern, and how it helps developers create great React apps.

· Web Dev Zone ·
Free Resource

Learn how Crafter’s Git-based content management system is reinventing modern digital experiences. Download this white paper now. 

Flux is a new kind of architecture that Facebook uses when it works with React. React - a popular front-end technology like AngularJS - is a Javascript framework, but it only works with the View layer, which means you have only the V in the MVC - Model-View-Controller - architecture. React gives you the template language and a few function hooks to render HTML. Since it is component based, you can compose an application with React, and just specify how you want your component to look. React will keep it updated; even the underlying data changes. The core principles of React are (1) Flexibility, (2) Efficiency and (3) Declarative code. As React is flexible, you can use it in several projects, create new apps, and even use it within the existing code base, without doing a rewrite.

What Is Flux?

We learned that React takes care of the V, or View, part of the MVC. Now, what about the M, or the Model, part? Flux, a programming pattern, takes care of that. It is the architecture responsible for creating data layers in JavaScript applications and building client-side web applications. Flux complements React’s Composable view components through its unidirectional data flow. You can also say that Flux is more of a pattern than a framework and it has four main components (we will go in depth later):

  • Dispatcher
  • Stores
  • Views (React components)
  • Action

This is not like the general MVC that you see in other frameworks. But yes, there are Controllers, but they are mostly Controller views. Views are at the top of the hierarchy and they retire the data and functionality and pass them down to their children.

Flux follows the concept of unidirectional data flow making it much easier to zero in on where the error lies. The data goes through a strict pipeline through your application. React and Flux are actually two of the most popular frameworks that follow the concept of unidirectional data flow.

While React makes uses of a virtual DOM object to render changes, Flux does it differently. In Flux, the interactions with the user interface will trigger a series of actions that would alter the application data. The View will get alerts on the changes.

Main Features of Flux

Flux is open source and more of a design pattern than a formal framework and you can use it immediately. What keeps it apart from other frameworks is that it is different from the MVC Design pattern.

Flux keeps code predictable when compared to other MVC frameworks. Developers can build applications without being bothered about complicated interactions between data resources.

Flux boasts of a better-structured data flow – unidirectional. Being unidirectional is the central feature of Flux. The actions are propagated to the new system with regard to user interactions. You can start using Flux without using a whole lot of new code, apart from React.

Flux vs. MVC

Now that we have covered both MVC and Flux patterns, the next question is, which one is a better choice?

There are different kinds of MVC patterns, but the basic concepts of each one remain the same:

  • Model - Maintains the behavior and data of an application domain.
  • View - The display of the model in UI.
  • Controller - Uses the user input, manipulates the model, and updates the view.

MVC Design Pattern

The main problem with MVC is that it doesn’t scale well for Facebook’s huge code base. Flux proved to be a better choice because it is all about tweaking the flow of the app.

MVC has stood the test of time, and ever since its launch in 1976, it has been the favorite for many developers. But MVC couldn’t handle the code base that Facebook needed, and, hence, Flux started ruling the roost. Let’s take a look at the main factors that give Flux the upper hand over the VC design pattern.

Flux Design Pattern

The Flow - Flux is quite strict about the flow of the application. The data Dispatcher puts forth some strict rules and exceptions to govern the flow. There is no such thing in MVC, and the flows are implemented differently.

Unidirectional Flow in Flux - While MVCs are bidirectional in their flow, in Flux all the changes go in the same direction through the data dispatcher. The store cannot change by itself, and this concept applies to all other Actions. Changes that need to be made have to go through the Dispatcher through Actions.

Store - While MVC cannot model single objects, Flux can do it to store any application-related data. When it comes to choosing Flux or MVC, Flux would be a better choice because it is very easy to understand and works with minimum code. Flux allows you to structure your app effectively. This is something to look forward to because React's programming language is integrated with a huge code base that is seemingly endless and a huge runtime complexity that developers just hate. Once you understand the cons of bidirectional data flow, it is easier to understand why unidirectional data flow is the best. In the bidirectional data flow, you have the typical data flow – Model-View-Controller. But when applications became more complex, the Controller begins to feel the burden.

The Controller has the huge responsibility of maintaining both the application state and the data. Also, the cascading updates make the app really difficult to understand and debug. In the end, you have an application whose results are totally unpredictable. With unidirectional data flow, this problem is mitigated, and, eventually, a predictable application state is achieved. When the data flow is unidirectional, changes in the application view layer will trigger an action in the data layer. These changes will then be reflected in the View. The View does not directly affect application data.

Flux Architecture and How it Works

The components in Flux's architecture interact more like an EventBus and less like an MVC. As mentioned earlier, Flux is not actually a library or a framework, it is a new kind of architecture that Facebook created to work with React. Hence the main function of Flux is to complement React and promote Unidirectional Data Flow.

Flux Application Architecture

Flux Application Architecture

In a typical Flux architecture, you will find the following components:

Actions - Helpers that pass data to the Dispatcher.

Dispatcher - Receives these Actions and broadcasts payloads to registered callbacks.

Stores - Act as containers for application state and logic. The real work in the application is done in the Stores. The Stores are registered to listen in on the actions of the Dispatcher and update the Views according to these actions.

Controller Views - React Components grab the state from the stores and then pass it down to the child components.

The Controllers in the MVC and Flux are different. In Flux, the Controllers are Controller-Views and are found at the very top of the hierarchy. Views are React components. All the functionality is usually found in the Store. The Store is where all the work is done and tells the Dispatcher which events/actions it is listening for.

When an event happens, the Dispatcher would send the “payload” to the Store that is registered to listen for that particular action. Now it is up to the Store to update the View, which in turn triggers an action. The action to that will occur is also predetermined, like name, the type of action, and so on.

The View propagates the Action through a central Dispatcher and this will be sent to various Stores. These Stores contain an application’s business logic and other data. It updates all the Views. It works best with React’s programming style and the Store sends updates without the need to provided detailed code on how to transition views between states.

This proves that the Flux pattern follows a unidirectional data flow. The Action, Dispatcher, Store, and View are independent nodes with specific inputs and outputs. The data flows through the Dispatcher, the central hub, which in turn manages all the data. The Dispatcher acts as a registry with registered callbacks that the Stores respond to. Stores will emit a change which will be picked by the Controller-Views.

This is what happens when a View is propagated in the system:

Flux Architecture - Working

This proves that there are no two-way bindings, the structure is akin to functional relative programming, and more something like flow-based programming. The dependencies that occur in the stores are kept in a strict hierarchy, while the Dispatcher handles the updates. This structure also solves the problems that come naturally with two-way binding. To create a Dispatcher, you need to bring the Dispatcher from the Flux. You can handle this by using libraries such as Dispatcher.js

A Detailed Overview of Dispatcher

The Dispatcher is a global pub or a sub handler that broadcasts payloads to registered callbacks. A dispatcher can easily manage dependencies between stores. This is not similar to generic pub-sub systems mainly because:

1. Callbacks are not subscribed to particular events. Each payload will be dispatched to each registered callback.

2. It is possible to defer in whole or in part until all the other callbacks have been executed.

Each Store registers a callback with the Dispatcher. When new data is entered, these callbacks are utilized to send the data to the Stores. This process of invoking the callbacks is done through the methoddispatch(). The function of the dispatch()method is to provide a simple, synchronous iteration through the callbacks, via turn-by-turn invoking. As the complexities of applications increase, the dependencies across different stores will definitely take place.

It means, for example, when Store B updates itself, we need a Dispatcher to automatically invoke the callback for Store C. The wait functionality is done via the methodwaitFor().

Here are APIs for Dispatcher functioning:

register(function callback): string - Registers a callback to be invoked with every dispatched payload. Comes up with the token to be used withwaitFor().

unregister(string id): void - When the requirement is to remove a callback (based on the token).

waitFor(array<string> ids): void - The store would wait for callbacks before continuing execution of the current callback. This is done only in response to a dispatched payload.

waitFor() gives a new iteration cycle over the dependencies. When all the dependencies are met, the original callback initiates. The methodwaitFor() can be used for various actions, for example, when Store A is waiting for a response from Store B. It is meant to wait for a specific action.

dispatch(object payload): void - Dispatches a payload to all registered callbacks.

isDispatching(): boolean - Answeres the question of whether this Dispatcher is currently dispatching data.

The following examples will give you a better understanding of how Dispatcher works. Consider a hypothetical situation of a flight destination form where a default city is selected when a country is selected.

var flightDispatcher = new Dispatcher();
// Keeps track of which country is selected
var CountryStore = {
    country: null
};
// Keeps track of which city is selected
var CityStore = {
    city: null
};
// Keeps track of the base flight price of the selected city
var FlightPriceStore = {
    price: null
};

When a user changes the selected city, the payload is dispatched:

dispatched.flightDispatcher.dispatch({
    actionType: 'city-update',
    selectedCity: 'amsterdam'
});

This payload is digested by CityStore

flightDispatcher.register(function(payload) {
    if (payload.actionType === 'city-update') {
        CityStore.city = payload.selectedCity;
    }
});

Now, when the user selects a country, the payload is dispatched:

dispatched: flightDispatcher.dispatch({
    actionType: 'country-update',
    selectedCountry: 'brazil'
});

This payload is digested by both stores:

CountryStore.dispatchToken = flightDispatcher.register(function(payload) {
    if (payload.actionType === 'country-update') {
        CountryStore.country = payload.selectedCountry;
    }
});

When the callback to update CountryStore is registered, a reference is saved to the returned token. Using this token with waitFor(), the CountryStore is updated before the callback that updates CityStore needs to query its data:

data.CityStore.dispatchToken = flightDispatcher.register(function(payload) {
    if (payload.actionType === 'country-update') {
        // `CountryStore.country` may not be updated.
        flightDispatcher.waitFor([CountryStore.dispatchToken]);
        // `CountryStore.country` is now guaranteed to be updated.
        // Select the default city for the new country
        CityStore.city = getDefaultCityForCountry(CountryStore.country);
    }
});

The usage of waitFor() can be chained like this:

this: FlightPriceStore.dispatchToken = flightDispatcher.register(function(payload) {
    switch (payload.actionType) {
        case 'country-update':
        case 'city-update':
            flightDispatcher.waitFor([CityStore.dispatchToken]);
            FlightPriceStore.price = getFlightPriceStore(CountryStore.country, CityStore.city);
            break;
    }
});

The country-update payload will be guaranteed to invoke the stores' registered callbacks in order: CountryStoreCityStore, then FlightPriceStore.

Credits: Facebook Code

A Quick Overview of Flux Utils

In order to work with Flux, you need to have a solid foundation. This is provided through certain basic utility classes. Flux Utils provides those. However, they are not entirely a feature-complete framework, and they may not be able to handle all use cases.

It is possible to rely on other great Flux frameworks if the existing utilities do not address your case. Keeping that in mind, you can check out these main classes.

  • Store

  • ReduceStore

  • Container

In order to import these base classes from,flux/utils follow this code:

import {ReduceStore} from 'flux/utils';
class CounterStore extends ReduceStore {
  getInitialState(): number {
    return 0;
  }
  reduce(state: number, action: Object): number {
    switch (action.type) {
      case 'increment':
        return state + 1;
      case 'square':
        return state * state;
      default:
        return state;
    }
  }
}

Credits: Facebook Code

Here are the best practices that need to be followed when using these classes.

Stores - The function is to cache data. As they never have public setters they expose public getters to access data. It is also the function of the store to respond to the actions sent by the Dispatcher. The stores always change when data changes. They emit changes only during a dispatch.

Actions - Here, the Actions mean describing user actions, and not setter’s actions; for example, select-pageand not set-page-id

Containers - They are React components that control the View. Their primary function is to gather the information from the store and save it. Containers do not have props or UI logic.

Views - They are also React components, but they are controlled by Containers. They have UI and rendering logic.

Popular Implementations of Flux

Now that you have got an overall idea of the Flux architecture, let’s take a look at a few of the famous Flux implementations.

Redux - According to GitHub, Redux is a predictable state container for JavaScript apps. Many of the concepts are similar to functional programming, and all the data is kept in a single store.

Irrespective of the application size, Redux is always a single object, quite unlike Flux, which keeps separate stores for different kinds of data. If there are manipulations to be made on the data, it doesn’t affect the state. In this way, the state is immutable.

All updates and manipulations are done on a state tree. But this does not make the application slow as the data can be shared among several versions of the state tree. The updates on the application state are done through Actions, which are plain objects themselves but contain a property depicting the kind of action performed. The data that describes the action will also be included.

The Store’s Dispatcher will dispatch the action and, from there, it goes to the Reducer, and then to the current state tree. Here, the actions of the application will be described, i.e. the different things the application can do. Just a single reducer would be enough for the state transformation and action.

  • Reflux - It is one of the most popular implementations of Flux. But there are some basic differences between the two. Reflux doesn’t use a Dispatcher; instead, each Action is a Dispatcher. As the Actions themselves are functions, there are no action creators. And the best thing about Reflux is that it is more concise and streamlined, with fewer requirements around repetitive code.

  • Fluxxor - Fluxxor makes use of a number of tools and Flux architecture to build JS data layers. In order to enjoy the full functionality of Fluxxor, you will have to make it work with React as the view layer.

  • Flummox - Flummox has three components, namely, Actions, Stores, and Flux. In Flummox, you create some Actions, then create a Store that is responsive to those Actions. In the next stage, you bring them together in Flux and use it in a View. Each of the Flummox components is represented by a class, and you extend from this base class.

  • Alt - Modeled after Flux, Alt is a library that facilitates the managing of state in JavaScript applications. You can install Alt if you are installing package managers like nom or bower. Alt gives you the benefit of Flux, but with a better syntax.

Wrap Up

The main idea behind using Flux architecture is to have a simple application architecture. This makes it easier to maintain and reason about when it gets more complex. As there are no ambiguities on the relationship between various components, work gets moving.

On top of that, Flux is consistent and repeatable, making it very logical to work with, while creating an Action. It is also easier when you want the Store to know how to handle the Action, store data, and activate change events.

Crafter CMS is a modern Git-based platform for building innovative websites and content-rich digital experiences. Download this white paper now.

Topics:
flux ,web dev ,web application development ,react.js ,redux

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}