React and Flux Tutorial

NodeJS tutorial using React and Flux with Webpack and Babel

Bully Spider vs Arachnerd

Learning React and Flux could be as intimidating as the green-hatted spider over there. Luckily, I’ve survived the bullying and decided to put together this tutorial. My decision to use Webpack and Babel is because its the best way to use React (in my opinion).

In a nutshell

Flux consists of Actions, the Dispatcher and the Store. The Views are the React. As the following diagram explains, The React components initiate an Action, which is then received by the Dispatcher. The Dispatcher determines the type of Action which is then communicated to the Store, which then updates the data and emits a change event (which the component listens for) and the Component then updates its state based on the current data in the store.

Flux Diagram

This is a basic tutorial showing the fundamentals of what each part of the React/Flux setup does. We will be using Webpack (a module bundler, like Browserify) to bundle everything into a client side script and Babel to transpile the ES6/ES2015 code used by React (If you want to see how React works without Flux, ES6, Babel and Webpack, check out this link).

Components/Views

A simple React component consists of a class with a constructor (which inherits props passed into the component), methods and the function that returns the actual HTML (or JSX in the context of React). The home component below is a simple page with a link to the main dashboard component. (Code is available on Github)

import React from ‘react’;
import { Link } from ‘react-router’;
export default class Home extends React.Component {
constructor(props) {
super( props );
}
render() {
return (
<div className=”container text-center”>
<div className=”home-login”>
<h1>Enter the tutorial</h1>
<Link to={‘/dashboard/test’} className=”btn btn-default btn-block”>Enter</Link>
<hr></hr>
</div>
</div>
);
}
}

The react-router contains the Link components needed to create links to other pages (components). The link in the snippet above takes you to the Dashboard component.

The constructor in the Dashboard component binds ‘this’ to the methods so that they can access the data. It also contains the initialisation of the state variable so that it can be accessed later.

constructor(props) {
super( props );
this.addThingFromComponent = this.addThingFromComponent.bind(this);
this._onChange = this._onChange.bind(this);

this.deleteThingFromComponent = this.deleteThingFromComponent.bind(this);

this.state = {};
}

The function componentDidMount() executes when the component is displayed. In this case, it calls the function from the Store that adds a change listener to the component and it executes the _onChange() function that gets the data from the store and sets the components state every time a change is made. The componentWillUnmount() function executes when the component is unmounted and it calls the function to remove the changeListener from the component.

componentDidMount() {
appStore.addChangeListener(this._onChange);
}
componentWillUnmount() {
appStore.removeChangeListener(this._onChange);
}
_onChange() {
this.setState(appStore.getList());
}

We will get more into detail about the Store (appStore.js) later in the tutorial. The following snippet from the Dashboard component contains the functions that manipulate the data in the component. The functions in the component call the actions to update the Store. The Store then emits a change event to the component, which then reloads itself with the new data.

addThingFromComponent() {
var thing = document.getElementById(‘thingName’).value;
var count = document.getElementById(‘thingCount’).value;
var add = true;
for (var x = 0; x < this.state.thingList.length; x++) {
if (this.state.thingList[x].thing === thing) {
add = false;
break;
} else {
add = true;
}
}
if (add) {
if (count < 0) {
console.log(‘Cannot submit a negative value’);
} else {
viewActions.addThing(
{
thing: thing,
count: count
}
);
console.log(“added”);
console.log(this.state.thingList);
}
} else {
console.log(“Thing already exists”);
}
}
deleteThingFromComponent(index) {
viewActions.removeThing(index);
}

Before we go into the Actions, we’re going to look at the rest of the Dashboard component. The render method contains the style variables as well as the return statement with the JSX/HTML. The buttons have an onClick prop that calls the respective functions defined in the component.

render() {
var backBtnStyle = {
float: “right”,
paddingTop: “5px”,
paddingBottom: “5px”,
margin: “5px”
}
return (
<div className=”container”>
<br></br>
<div className=”home-login”>
<small>Params passed into props.params: {this.props.params.thing </small>
<Link to={‘/’} style={backBtnStyle} className=”btn btn-default”>BACK</Link>
<hr/>
<div id=”child-page-items” className=”list-group”>
<p>This tutorial serves to better your understanding of how React and Flux fit together.</p>
</div>
<div className=”box”>
<table className=”table table-bordered text-center”>
<thead>
<tr>
<th className=”text-center”>Thing</th>
<th className=”text-center”>Count</th>
<th></th>
</tr>
</thead>
<tbody>
   {this.state.thingList.map(function(item, index) {
var boundDelete = this.deleteThingFromComponent.bind(this, index);
return (
<tr key={index}>
<td>{item.thing}</td>
<td>{item.count}</td>
<td className=”col-xs-2"><button style={backBtnStyle} onClick={boundDelete} className=”btn btn-danger” >Remove</button></td>
</tr>
)
}, this)}
</tbody>
</table>
</div>
 <div className=”col-xs-4">
<Input id=”thingName” className=”thingChooser” type=”select” placeholder=”select” >
<option value=”thing1">thing1</option>
<option value=”thing2">thing2</option>
</Input>
</div>
   <div className=”col-xs-4">
<Input id=”thingCount” className=”thingChooser” defaultValue={0} type=”number”/>
</div>
   <div className=”col-xs-4">
<button type=”button” className=”btn btn-default btn-block btn-sm” onClick={ this.addThingFromComponent }><i className=”fa fa-plus”></i> Add</button>
</div>
</div>
</div>
);
}

Actions and the Dispatcher

The add and delete methods contained in the dashboard component call methods from the viewActions file. Lets take a look at what it does:

var AppDispatcher = require(‘../dispatcher/AppDispatcher’);
var appConstants = require(‘../constants/appConstants’);
var viewActions = { 
  addThing: function(storeData) {            
    AppDispatcher.handleViewAction( 
{
actionType: appConstants.ADD_THING,
data: storeData
});
},
  removeThing: function(index) { 
   AppDispatcher.handleViewAction(
{
actionType: appConstants.REMOVE_THING,
data: index
});
}
};
module.exports = viewActions;

The add and remove methods in the viewActions file notifies the dispatcher of the action, along with any data (payload) required. The dispatcher then handles the action and updates the store with the new data. The code for the dispatcher is as follows:

var Dispatcher = require(‘flux’).Dispatcher;
var AppDispatcher = new Dispatcher();
AppDispatcher.handleViewAction = function(action) { 
this.dispatch(
{
source: ‘VIEW_ACTION’,
action: action
});
}
AppDispatcher.handleServerAction = function(action) { 
this.dispatch(
{
source: ‘SERVER_ACTION’,
action: action
});
}
module.exports = AppDispatcher;

We only use viewAction and serverAction to determine when you are loading data from an API and need to determine whether the action is coming from the client or the server.

The Store

The store is where all the magic happens. All the manipulation of data is done here:

var AppDispatcher = require(‘../dispatcher/AppDispatcher’);
var appConstants = require(‘../constants/appConstants');
var objectAssign = require(‘react/lib/Object.assign’);
var EventEmitter = require(‘events’).EventEmitter; 
var CHANGE_EVENT = ‘change’;
var _store = { thingList: []}; 
var appStore = objectAssign({}, EventEmitter.prototype, { 
addChangeListener: function(cb) {
this.on(CHANGE_EVENT, cb);
},

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

getList: function() {
return _store;
}
});

The appStore object contains the methods for the changeListeners as well as the methods that returns the store.

When the Store receives an action from the AppDispatcher, it updates the store with the payload (data). Upon updating, the store emits a change event which then triggers the component to call the getList() method in the store to update the components state with the updated store data.

AppDispatcher.register(function(payload) { 
var action = payload.action;
switch(action.actionType) {
case appConstants.ADD_THING:
var data = action.data;
_store.thingList.push({
thing: data.thing,
count: data.count
});
appStore.emit(CHANGE_EVENT);
break;
case appConstants.REMOVE_THING:
var data = action.data;
_store.thingList.splice(data.index, 1);
appStore.emit(CHANGE_EVENT);
break;
default:
return true;
}
});
module.exports = appStore;

Conclusion

I hope you enjoyed reading this article and I hope it helped with understanding how React and Flux works. If it helped you or if you have any questions, concerns or improvements, please comment, like and share this article so it can help someone else too :)