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

React.js for Noobs Part II: Flux

DZone's Guide to

React.js for Noobs Part II: Flux

In this article, we continue our deep dive into React.js by looking at Flux, and how it can be used in React-based web applications.

· Web Dev Zone ·
Free Resource

Jumpstart your Angular applications with Indigo.Design, a unified platform for visual design, UX prototyping, code generation, and app development.

This post is a continuation of my earlier React.js for Noobs post; if you haven’t read it already, first go through it before continuing with this article. React is a library to manipulate the web page DOM, and, when building a complex web app, it is paramount to manage the state of the application as most of the time React components want to talk with each other.

If all the components start to talk with each other, the whole app will be a mess and debugging an issue will be a nightmare. Hence, Flux can be used to make our lives easier. Flux is a data unidirectional architecture pattern. It is mostly a concept, rather than an implementation. First, let’s get a good conceptual understanding of what Flux is and later on move on to an example implementation to get hands-on experience.

Flux in a Nutshell

Flux's architecture pattern mainly consists of four components:

  1. View

  2. Action

  3. Dispatcher

  4. Store

Let’s start with the View. The view is the user-visible layer which consists of React Components. Users can interact with React components since a component is mostly an HTML segment rendered on the web page. When a user interacts with a component an action can be invoked, such as showing a pop-up message when a button is clicked, and changing the color of a DIV element on mouse hover and so on.

This moves to the next component, Action. In the above-mentioned example, a user clicking on the button is the event and showing a pop-up message is the action. There can be predefined actions and for each event a React component can invoke an action.

When an action is invoked, the dispatcher dispatches the action to the store. The dispatcher is responsible for the wiring of React components and Stores via actions. That means whenever a component invokes an action, the dispatcher will dispatch the action to all the registered stores.

The next component is the Store, which is just an object extended from the event emitter which acts as a data store. This data store keeps the state of the complete application or the state of a part of the application. When an action is dispatched to the store, the store can change its state based on the action. As an example, when an action to change the color of a DIV is dispatched, the store can change its current state to reflect the new color for the DIV.

Now comes the last part. When a store changes its state, how can the React components update their component states so that ultimately the change is reflected on the web page? This is done by event listeners. A react component can register an event listener for a particular store and when the store changes its state, it will emit an event. The React Component, which is listening to these events, will be notified and, no, the component can query the new state of the store and update its component state. The below diagram summarizes the connection between all these components.

Image title

Flux in Action

In this hands-on tutorial, we are going to build a basic React app with two components as shown in figure 2 and see how these two components can communicate with each other.


Image title

First, let’s create a React app by executing the below command:

create-react-app flux-example 

Next, let’s add a Flux dependency to our app by executing the below command:

npm install --save flux 

For this example app, apart from the boilerplate code, we need two components. First, let’s create a directory called components (in the src directory) and within that create a file named ButtonComponent.js with the below content.

import React from "react";

export default class ButtonComponent extends React.Component {

    render() {
        return (
            <div>
                <button className="color-button">Red</button>
                <button className="color-button">Blue</button>
            </div>
        );
    }
}

As you can see, the component will render two buttons with Red and Blue labels. Next, within the same directory, create a ColorComponent.js file with the below content.

import React from "react";

export default class ColorComponent extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            color: "lightgrey"
        }
    }
    
    render() {
        return (
            <div className="color-container" style={{backgroundColor: this.state.color}}/>
        );
    }
}

As you can see, the component just renders a DIV with light grey background color. Next, modify the content of the App.js file as shown below.

import React from "react";
import ButtonComponent from "./components/ButtonComponent";
import ColorComponent from "./components/ColorComponent";

export default class App extends React.Component {

    render() {
        return (
            <div>
                <ButtonComponent/>
                <ColorComponent/>
            </div>

        );
    }
}

Further, after adding the below styles to the index.css file and executing npm start and opening http://localhost:3000/ in your browser, you should see the content of the app which is similar to figure 2.

.color-container {
    width: 400px;
    height: 400px;
}

.color-button {
    width: 50px;
    height: 20px;
    margin: 10px;
    background-color: #dddddd;
}

Although we have the skeleton of our app configured, clicking on the buttons will not make any change to our color component. To address that, first, create a Dispatcher.js file in the src directory with the below content.

import {Dispatcher} from "flux";

export default new Dispatcher();

This is the dispatcher for our app which will dispatch actions invoked in components to the registered stores. Now when the user clicks on a button we need an action to be invoked. For that, create a directory named actions (within the src directory) and, within that, create a file named ColorAppActions.js with the below content.

import dispatcher from "../Dispatcher";

export const COLOR_APP_ACTIONS = {
    CHANGE_COLOR: 'colorAppActions.ChangeColor'
};

export function changeColor(colorName) {
    dispatcher.dispatch({
        type: COLOR_APP_ACTIONS.CHANGE_COLOR,
        value: colorName
    })
}

You can create an action file for each component or only one file for your complete app. But it would be easier to maintain the app by keeping a separate action file for each main component. In the above code, as you can see, the changeColor(colorName) function is exposed, so that our ColorComponent can use it to notify the change of color. Within that function, we invoke the dispatch() method of the dispatcher with an object with two keys as a method argument. The type represents a unique name (throughout the app) to distinguish the action and value contains the additional information associated with the action. You can include any number of additional key-value pairs in this object. Next, we need to update our ButtonComponent as shown below.


import React from "react";
import * as ColorAppActions from "../actions/ColorAppActions";

export default class ButtonComponent extends React.Component {

    onButtonClick = (colorName) => {
        ColorAppActions.changeColor(colorName)
    };

    render() {
        return (
            <div>
                <button onClick={() => this.onButtonClick("red")} className="color-button">Red</button>
                <button onClick={() => this.onButtonClick("blue")} className="color-button">Blue</button>
            </div>

        );
    }
}

When the user clicks on a button, we have configured the component to invoke the onButtonClick method through the onClick synthetic event. Further, for each invocation, we pass the name of the color as a method argument. Within the onButtonClick method, we invoke our changeColor() action.

Next, let’s move on to the store. Create a directory named stores (within the src directory) and create a file named ColorAppStore.js within that directory with the below content.

import dispatcher from "../Dispatcher";
import {EventEmitter} from "events";
import * as ColorAppActions from "../actions/ColorAppActions";

class ColorAppStore extends EventEmitter {

    constructor() {
        super();
        this.activeColor = "lightgrey";
    }

    handleActions(action) {
        switch (action.type) {
            case ColorAppActions.COLOR_APP_ACTIONS.CHANGE_COLOR: {
                this.activeColor = action.value;
                this.emit("storeUpdated");
                break;
            }
            default: {
            }
        }
    }

    getActiveColor() {
        return this.activeColor;
    }
}

const colorAppStore = new ColorAppStore();
dispatcher.register(colorAppStore.handleActions.bind(colorAppStore));
export default colorAppStore;

This is the data store of our app. As you can see, it is extended from EventEmitter because once the data in the store changes, the store will emit events so that subscribed components can update their respective states. In this store, we have only one piece of data at the moment, activeColor, which represents the current background color of the ColorComponent.

const colorAppStore = new ColorAppStore();

dispatcher.register(colorAppStore.handleActions.bind(colorAppStore));

As for any store, the above two lines are salient. The first one creates a new instance of the store and the second one registers the store with the dispatcher through a handler function so that whenever the dispatcher fetches an action, the handler function of the store will be invoked with the respective action as a method argument to the handler function. In this example, the handleActions() method in the store will be invoked for any action invocation.

Within the handleActions() method, the store can query for the identifier of the action (type) and if the action is relevant to the store then the store can act upon it or, if that is not the case, simply ignore the action altogether. In our case, for COLOR_APP_ACTIONS.CHANGE_COLOR we want the store to modify its activeColor accordingly and emit an event to notify the components that a change in the store has occurred. This is accomplished through the below code.

this.activeColor = action.value;

this.emit(“storeUpdated”);

Next, we need to update our ColorComponent as shown below to catch events emitted by the store.

import React from "react";
import ColorAppStore from "../stores/ColorAppStore";

export default class ColorComponent extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            color: ColorAppStore.getActiveColor()
        }
    }

    componentDidMount() {
        ColorAppStore.on("storeUpdated", this.updateBackgroundColor);
    }

    componentWillUnmount() {
        ColorAppStore.removeListener("storeUpdated", this.updateBackgroundColor);
    }

    updateBackgroundColor = () => {
        this.setState({color: ColorAppStore.getActiveColor()})
    };

    render() {
        return (
            <div className="color-container" style={{backgroundColor: this.state.color}}/>
        );
    }
}

The first change in the component is that in the constructor for the initial state we obtain the color from the store so that we would have a single source for the background color. Next, within the componentDidMount() lifecycle method, the component is subscribing to storeUpdated events emitted from the ColorAppStore. It is mandatory to remove the listener in the componentWillUnmount() method. Otherwise, the store subscription will cause a memory leak in your app. As per the above code, when the ColorAppStore emits the storeUpdated event, the updateBackgroundColor() method in the component will be invoked and within that method, we update the current state of the component to reflect the change in the store.

Now, if you run the app and click on the buttons you will see the background color of the ColorComponent change, as shown below.

Image title

In a nutshell, when the user clicks on a button, an action will be invoked by ButtonComponent with the respective color as data and the dispatcher will fetch this action to the ColorAppStore. Next, the store will extract the color information from the action and update its store data and emit a storeUpdated event. The ColorComponent, which is listening to the storeUpdated events from the store, will receive the emitted event and the component will update its internal state to match with the data in the ColorAppStore.

As you can see, the store acts as the single source of truth and the components talk with each other through manipulating the data within the store. This example demonstrates the basic usage of pure flux and there are better implementations of flux which abstract out the boilerplate code to increase developer efficiency. One such implementation is Redux and I will walk you through the basics of Redux in the next article.

The complete source code of this example app is available at https://github.com/sajithdilshan/flux-example

Take a look at the Indigo.Design sample applications to learn more about how apps are created with design to code software.

Topics:
react ,flux ,javascript ,react.js ,web dev

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}