read

Added a new section on sourcemaps in Step 2.

Tutorial has been updated to use Babel 6.0 and React Router 1.0. For detailed tutorial updates see November 12, 2015 notes below.

Tutorial has been updated to use React 0.14 and React Router 1.0-rc3 that introduced breaking changes. For detailed tutorial updates see October 19, 2015 notes below.

Table of Contents

Overview

In this tutorial we are going to build a character voting app (inspired by Facemash and Hot or Not) for EVE Online - a massively multiplayer online game. Be sure to play this awesome soundtrack below to get yourself in the mood for this epicly long tutorial.

While listening to this soundtrack, imagine yourself mining asteroid belts in deep space while keeping a lookout for pirates on the radar, researching propulsion system blueprints at the station’s facility, manufacturing spaceship components for capital ships, placing buy & sell orders on the entirely player-driven market where supply and demand govern the game economics, hauling trade goods from a remote solar system in a massive freighter, flying blazingly fast interceptors with a microwarpdrive or powerful battleships armored to the teeth, optimizing extraction efficiency of rare minerals from planets, or fighting large-scale battles with thousands of players from multiple alliances. That is EVE Online.

Each player in EVE Online has a 3D avatar representing their character. This app is designed for ranking those avatars. Anyway, your goal here is to learn about Node.js, React and Flux, not EVE Online. But I will say this: “Having an interesting tutorial project is just as important, if not more so, than the main subject of the tutorial”. The only reason I built the original New Eden Faces app is to learn Backbone.js and the only reason I built the TV Show Tracker app is so that I could learn AngularJS. To me, either one of these projects is far more interesting than a simple todo app that everyone seems to be using these days.

One thing that I have learned — between screencasts, books and training videos, nothing is more effective than building a small project that you are passionate about to learn a new technology.

In the same spirit as my previous tutorials (TV Show Tracker and Instagram Clone), this is a step by step full-stack JavaScript tutorial where we build a complete app from the ground up.

Note This is a remake of the original This is a remake of the original New Eden Faces (2013) project, which was my first ever single-page application written in Backbone.js. It has been running in production on OpenShift with Node.js 0.8.x for over 2 years now.

I usually make as few assumptions as possible about a particular topic, which is why my tutorials are so lengthy, but having said that, you need to have at least some prior experience with client-side JavaScript frameworks and Node.js to get the most out of this tutorial.

Before proceeding, you will need to download and install the following tools:

Step 1. New Express Project

Create a new directory newedenfaces. Inside, create 2 empty files package.json and server.js using your favorite text editor or using the command line:

July 22, 2015 Update: I am using the default Terminal app in Mac OS X with Monokai theme and oh-my-fish framework for the Fish shell.

Open package.json and paste the following:

{ "name": "newedenfaces", "description": "Character voting app for EVE Online", "version": "1.0.0", "repository": { "type": "git", "url": "https://github.com/sahat/newedenfaces-react" }, "main": "server.js", "scripts": { "start": "node server.js", "watch": "nodemon server.js" }, "babel": { "presets": ["es2015", "react"] }, "dependencies": { "alt": "^0.17.8", "async": "^1.5.0", "body-parser": "^1.14.1", "colors": "^1.1.2", "compression": "^1.6.0", "express": "^4.13.3", "history": "^1.13.0", "mongoose": "^4.2.5", "morgan": "^1.6.1", "react": "^0.14.2", "react-dom": "^0.14.2", "react-router": "^1.0.0", "request": "^2.65.0", "serve-favicon": "^2.3.0", "socket.io": "^1.3.7", "swig": "^1.4.2", "underscore": "^1.8.3", "xml2js": "^0.4.15" }, "devDependencies": { "babel-core": "^6.1.19", "babel-preset-es2015": "^6.1.18", "babel-preset-react": "^6.1.18", "babel-register": "^6.3.13", "babelify": "^7.2.0", "bower": "^1.6.5", "browserify": "^12.0.1", "gulp": "^3.9.0", "gulp-autoprefixer": "^3.1.0", "gulp-concat": "^2.6.0", "gulp-cssmin": "^0.1.7", "gulp-if": "^2.0.0", "gulp-less": "^3.0.3", "gulp-plumber": "^1.0.1", "gulp-sourcemaps": "^1.6.0", "gulp-uglify": "^1.4.2", "gulp-util": "^3.0.7", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", "watchify": "^3.6.0" }, "license": "MIT" }

October 19, 2015 Update: Updated package versions and added two new packages: react-dom (as part of the React 0.14 changes) and history (as part of the React Router 1.0 changes).

November 12, 2015 Update: Added babel configuration presets (new in Babel 6.x). No longer uses babel-node command to start or watch the server, instead you can use node and nodemon commands directly. Updated existing packages, removed unused packages, added new Babel 6.x packages.

These are all the packages that we will be using in this project. Let’s briefly go over each package.

Package Name Description alt Flux library for React. async For managing asynchronous flow. body-parser For parsing POST request data. colors Pretty console output messages. compression Gzip compression. express Web framework for Node.js. history Manage session history in browsers, used by react-router. mongoose MongoDB ODM with validation and schema support. morgan HTTP request logger. react React. react-dom React rendering, it is no longer bundled with React. react-router Routing library for React. request For making HTTP requests to EVE Online API. serve-favicon For serving favicon.png icon. socket.io To display how many users are online in real-time. swig To render the initial HTML template. underscore Helper JavaScript utilities. xml2js For parsing XML response from EVE Online API.

Run npm install in the Terminal to install the packages that we specified in the package.json.

Note If you are using Windows check out If you are using Windows check out cmder console emulator. It is the closest thing to Mac OS X/Linux Terminal experience.

Open server.js and paste the following code. It’s a very minimal Express application, just enough to get us started.

var express = require ( 'express' ); var path = require ( 'path' ); var logger = require ( 'morgan' ); var bodyParser = require ( 'body-parser' ); var app = express (); app . set ( 'port' , process . env . PORT || 3000 ); app . use ( logger ( 'dev' )); app . use ( bodyParser . json ()); app . use ( bodyParser . urlencoded ({ extended : false })); app . use ( express . static ( path . join ( __dirname , 'public' ))); app . listen ( app . get ( 'port' ), function () { console . log ( 'Express server listening on port ' + app . get ( 'port' )); });

Note Although we will be building the React app in ES6, I have decided to use ES5 here because this back-end code is mostly unchanged from when I built the original Although we will be building the React app in ES6, I have decided to use ES5 here because this back-end code is mostly unchanged from when I built the original New Eden Faces app. Furthermore, if you are using ES6 for the first time, it won't be too overwhelming, since the Express app should still be familiar to you.

Next, create a new directory public. This is where we are going to place images, fonts, compiled CSS and JavaScript files.

Run npm start in the Terminal to make sure our Express app is working without any issues.

You should see Express server listening on port 3000 message in the Terminal.

Step 2. Build System

If you have been around in the web community at all, then you may have heard about Browserify and Webpack tools. If not, then consider what it would be like having to manually include all these <script> tags in a specific order, because one file may depend on another file which depends on another file.

Additionally, we cannot use ECMAScript 6 directly in the browsers yet. Our code needs to be transformed by Babel into ECMAScript 5 before it can be served and interpreted by the browsers.

We will be using Gulp and Browserify in this tutorial instead of Webpack. I will not advocate for which tool is better or worse, but personally I found that Gulp + Browserify is more straightforward to me than an equivalent Webpack config file. I have yet to find a React boilerplate project with an easy to understand webpack.config.js file.

Create a new file gulpfile.js and paste the following code:

var gulp = require ( 'gulp' ); var gutil = require ( 'gulp-util' ); var gulpif = require ( 'gulp-if' ); var autoprefixer = require ( 'gulp-autoprefixer' ); var cssmin = require ( 'gulp-cssmin' ); var less = require ( 'gulp-less' ); var concat = require ( 'gulp-concat' ); var plumber = require ( 'gulp-plumber' ); var buffer = require ( 'vinyl-buffer' ); var source = require ( 'vinyl-source-stream' ); var babelify = require ( 'babelify' ); var browserify = require ( 'browserify' ); var watchify = require ( 'watchify' ); var uglify = require ( 'gulp-uglify' ); var sourcemaps = require ( 'gulp-sourcemaps' ); var production = process . env . NODE_ENV === 'production' ; var dependencies = [ 'alt' , 'react' , 'react-dom' , 'react-router' , 'underscore' ]; /* |-------------------------------------------------------------------------- | Combine all JS libraries into a single file for fewer HTTP requests. |-------------------------------------------------------------------------- */ gulp . task ( 'vendor' , function () { return gulp . src ([ 'bower_components/jquery/dist/jquery.js' , 'bower_components/bootstrap/dist/js/bootstrap.js' , 'bower_components/magnific-popup/dist/jquery.magnific-popup.js' , 'bower_components/toastr/toastr.js' ]). pipe ( concat ( 'vendor.js' )) . pipe ( gulpif ( production , uglify ({ mangle : false }))) . pipe ( gulp . dest ( 'public/js' )); }); /* |-------------------------------------------------------------------------- | Compile third-party dependencies separately for faster performance. |-------------------------------------------------------------------------- */ gulp . task ( 'browserify-vendor' , function () { return browserify () . require ( dependencies ) . bundle () . pipe ( source ( 'vendor.bundle.js' )) . pipe ( buffer ()) . pipe ( gulpif ( production , uglify ({ mangle : false }))) . pipe ( gulp . dest ( 'public/js' )); }); /* |-------------------------------------------------------------------------- | Compile only project files, excluding all third-party dependencies. |-------------------------------------------------------------------------- */ gulp . task ( 'browserify' , [ 'browserify-vendor' ], function () { return browserify ({ entries : 'app/main.js' , debug : true }) . external ( dependencies ) . transform ( babelify , { presets : [ 'es2015' , 'react' ] }) . bundle () . pipe ( source ( 'bundle.js' )) . pipe ( buffer ()) . pipe ( sourcemaps . init ({ loadMaps : true })) . pipe ( gulpif ( production , uglify ({ mangle : false }))) . pipe ( sourcemaps . write ( '.' )) . pipe ( gulp . dest ( 'public/js' )); }); /* |-------------------------------------------------------------------------- | Same as browserify task, but will also watch for changes and re-compile. |-------------------------------------------------------------------------- */ gulp . task ( 'browserify-watch' , [ 'browserify-vendor' ], function () { var bundler = watchify ( browserify ({ entries : 'app/main.js' , debug : true }, watchify . args )); bundler . external ( dependencies ); bundler . transform ( babelify , { presets : [ 'es2015' , 'react' ] }); bundler . on ( 'update' , rebundle ); return rebundle (); function rebundle () { var start = Date . now (); return bundler . bundle () . on ( 'error' , function ( err ) { gutil . log ( gutil . colors . red ( err . toString ())); }) . on ( 'end' , function () { gutil . log ( gutil . colors . green ( 'Finished rebundling in' , ( Date . now () - start ) + 'ms.' )); }) . pipe ( source ( 'bundle.js' )) . pipe ( buffer ()) . pipe ( sourcemaps . init ({ loadMaps : true })) . pipe ( sourcemaps . write ( '.' )) . pipe ( gulp . dest ( 'public/js/' )); } }); /* |-------------------------------------------------------------------------- | Compile LESS stylesheets. |-------------------------------------------------------------------------- */ gulp . task ( 'styles' , function () { return gulp . src ( 'app/stylesheets/main.less' ) . pipe ( plumber ()) . pipe ( less ()) . pipe ( autoprefixer ()) . pipe ( gulpif ( production , cssmin ())) . pipe ( gulp . dest ( 'public/css' )); }); gulp . task ( 'watch' , function () { gulp . watch ( 'app/stylesheets/**/*.less' , [ 'styles' ]); }); gulp . task ( 'default' , [ 'styles' , 'vendor' , 'browserify-watch' , 'watch' ]); gulp . task ( 'build' , [ 'styles' , 'vendor' , 'browserify' ]);

February 15, 2016 Update: Replaced gulp-streamify with vinyl-buffer.

February 2, 2016 Update: Added gulp-sourcemaps plugin.

November 12, 2015 Update: Updated babelify transform to use es2015 and react presets.

Note If you have not used Gulp before, this is a great starting point — If you have not used Gulp before, this is a great starting point — An Introduction to Gulp.js

Although the code should be more or less self-explanatory with those task names and code comments, let’s briefly go over each task for completeness.

Gulp Task Description vendor Concatenates all JS libraries into one file. browserify-vendor For performance reasons, NPM modules specified in the dependencies array are compiled and bundled separately. As a result, bundle.js recompiles a few hundred milliseconds faster. browserify Compiles and bundles just the app files, without any external modules like react and react-router. browserify-watch Essentially the same task as above but it will also listen for changes and re-compile bundle.js. styles Compiles LESS stylesheets and automatically adds browser prefixes if necessary. watch Re-compiles LESS stylesheets on file changes. default Runs all of the above tasks and starts watching for file changes. build Runs all of the above tasks then exits.

Sourcemaps

What are sourcemaps? Why do we need them? Sourcemaps, as the name implies, map a concatenated/minified file back to an original state (source file). We have two tasks above — browserify and browserify-watch that will generate a single file called bundle.js . This file is not meant to be human-readable, but rather interpreted by the browsers. So what happens when an error is thrown and you are trying to debug the problem? Consider the following example:

Notice that an error has occurred on a line 2088 that does not match any of our source files. Clicking on it, reveals some details about the problem. In this particular case, you can easily figure out where it happened, especially if the code is not minified/mangled. Not shown here, but if I were to scroll up a little, there is a function called Stats, which would immediately tell you that the problem is in the Stats component.

But it is a completely different story when you are working on a large React application, consisting of hundreds of components, written by many different people, spanning thousands and thousands lines of code.

After adding sourcemaps support in the gulpfile.js above, here is how it would look like in the same scenario. First, a new file with a *.js.map extension is generated.

Next, both Google Chrome and Mozilla Firefox will automatically try to load CSS and JavaScript sourcemaps, when available.

That’s much better! It tells you not only in which file an error was thrown, but the exact line number as well. Clicking on the error above shows us the original source code.

Note: You may want to modify Gulp tasks above so that sourcemaps are generated only in development mode, similar to how code is minified code via gulp-uglify only in production mode.

Next, we will shift focus to the project structure by creating files and folders that gulpfile.js is expecting.

Step 3. Project Structure

In the public directory create 4 new folders css, js, fonts and img. Also, download this favicon.png and place it here as well.

It will show that an error is thrown on some line that does not exist in any of your source files. Clicking on the error, will reveal the details of where it has occurred.

In the newedenfaces directory (project root), create a new folder app.

Then inside app create 4 new folders actions, components, stores, stylesheets and 3 empty files alt.js, routes.js and main.js.

In the stylesheets directory create a new file main.less which we will populate with CSS styles shortly.

Back in the project root directory (newedenfaces), create a new file bower.json and paste the following:

{ "name" : "newedenfaces" , "dependencies" : { "jquery" : "^2.1.4" , "bootstrap" : "^3.3.5" , "magnific-popup" : "^1.0.0" , "toastr" : "^2.1.1" } }

Note Bower is a package manager that lets you easily download JavaScript libraries, such as the ones specified above, via a command line instead of visiting each individual website, downloading, extracting and adding it to the project manually. Bower is a package manager that lets you easily download JavaScript libraries, such as the ones specified above, via a command line instead of visiting each individual website, downloading, extracting and adding it to the project manually.

Run bower install and wait for the packages to be downloaded and installed into the bower_components directory. You can change that path using the .bowerrc file, but for the purposes of this tutorial we will stick with the defaults.

Similarly to node_modules, you should not commit bower_components into a Git repository. But hold on, if we don’t commit it to Git, how will those files be loaded when you deploy your app? We will revisit this issue later during the deployment step of this tutorial.

Copy all glyphicons fonts from bower_components/bootstrap/fonts into public/fonts directory.

Download and extract the following background images and place them into public/img directory:

Fun Fact I have used the Gaussian blur in Adobe Photoshop in order to create that out of focus effect over 3 years ago when I built the original New Eden Faces project, but now it should be totally possible to achieve a similar effect using I have used the Gaussian blur in Adobe Photoshop in order to create that out of focus effect over 3 years ago when I built the original New Eden Faces project, but now it should be totally possible to achieve a similar effect using CSS filters

Open main.less that we just created and paste the following styles from the link below. Due to the sheer length of it, I have decided to include it as a separate file.

If you have used the Bootstrap CSS framework in the past, then most of it should be already familiar to you.

I don’t know if you are aware of the latest trend to include styles directly inside React components, but I am not sure if I like this new practice. Perhaps when tooling gets better I will revisit this topic, until then I will use external stylesheets like I always have been. However, if you are interested in using modular CSS, check out css-modulesify.

October 19, 2015 Update: If you are building reusable React components like Elemental UI and Material UI then by all means do it. Personally, I would actually prefer if I don’t have to import accompanying “vendor” stylesheets, as we do with just much every user-interface jQuery library.

Before we jump into building the React app, I have decided to dedicate the next three sections to ES6, React, Flux, otherwise it may be too overwhelming trying to learn everything at once. Personally, I had a very hard time following some React + Flux code examples written in ES6 because I was learning a new syntax, a new framework and a completely unfamiliar app architecture all at once.

Since I cannot cover everything in-depth, we will be going over only those topics that you need to know for this tutorial.

Step 4. ES6 Crash Course

The best way to learn ES6 is by showing an equivalent ES5 code for every ES6 example. Again, I will only be covering what you need to know for this tutorial. There are plenty of blog posts that go in great detail about the new ES6 features.

Modules (Import)

// ES6 import React from 'react' ; import { Route , DefaultRoute , NotFoundRoute } from 'react-router' ;

// ES5 var React = require ( 'react' ); var Router = require ( 'react-router' ); var Route = Router . Route ; var DefaultRoute = Router . DefaultRoute ; var NotFoundRoute = Router . NotFoundRoute ;

Using the ES6 destructuring assignment we can import a subset of a module which can be quite useful for modules like react-router and underscore where it exports more than one function.

One thing to keep in mind is that ES6 imports are hoisted. All dependent modules will be loaded before any of the module code is executed. In other words, you can’t conditionally load a module like with CommonJS. That did throw me off a little when I tried to import a module inside an if-else condition.

For a detailed overview of the import statement see this MDN page.

Modules (Export)

// ES6 function Add ( x ) { return x + x ; } export default Add ;

// ES5 function Add ( x ) { return x + x ; } module . exports = Add ;

To learn more about ES6 modules, as well as different ways of importing and exporting functions from a module, check out ECMAScript 6 modules and Understanding ES6 Modules.

Classes

ES6 classes are nothing more than a syntactic sugar over the existing prototype-based inheritance in JavaScript. As long as you remember that fact, the class keyword will not seem like a foreign concept to you.

// ES6 class Box { constructor ( length , width ) { this . length = length ; this . width = width ; } calculateArea () { return this . length * this . width ; } } let box = new Box ( 2 , 2 ); box . calculateArea (); // 4

// ES5 function Box ( length , width ) { this . length = length ; this . width = width ; } Box . prototype . calculateArea = function () { return this . length * this . width ; } var box = new Box ( 2 , 2 ); box . calculateArea (); // 4

With ES6 classes you can now use extends to create a subclass from an existing class:

// ES6 class MyComponent extends React . Component { // Now MyComponent contains all React component methods // such as componentDidMount(), render() and etc. }

// ES5 var MyComponent = React . createClass ({ // Now MyComponent contains all React component methods // such as componentDidMount(), render() and etc. })

October 19, 2015 Update: Added the ES5 example using React.createClass .

For more information about ES6 classes visit Classes in ECMAScript 6 blog post.

var vs let

The only difference between the two is that var is scoped to the nearest function block and let is scoped to the nearest enclosing block - which could be a function, a for-loop or an if-statement block.

Here is a good example showing the difference between var and let :

var a = 5 ; var b = 10 ; if ( a === 5 ) { let a = 4 ; // The scope is inside the if-block var b = 1 ; // The scope is inside the function console . log ( a ); // 4 console . log ( b ); // 1 } console . log ( a ); // 5 console . log ( b ); // 1

Basically, let is block scoped, var is function scoped.

Arrow Functions (Fat Arrow)

An arrow function expression has a shorter syntax compared to function expressions and lexically binds the this value.

// ES6 [ 1 , 2 , 3 ]. map ( n => n * 2 ); // [2, 4, 6] // ES5 [ 1 , 2 , 3 ]. map ( function ( n ) { return n * 2 ; }); // [2, 4, 6]

Note Parentheses around the single argument are optional, so it is up to you whether you want to enforce it or not. Some see it as a bad practice, others think it's fine. Parentheses around the single argument are optional, so it is up to you whether you want to enforce it or not. Some see it as a bad practice, others think it's fine.

Besides a shorter syntax, what else is it useful for?

Consider the following example, straight from this project before I converted it to ES6.

$ . ajax ({ type : 'POST' , url : '/api/characters' , data : { name : name , gender : gender } }) . done ( function ( data ) { this . setState ({ helpBlock : data . message }); }. bind ( this )) . fail ( function ( jqXhr ) { this . setState ({ helpBlock : jqXhr . responseJSON . message }); }. bind ( this )) . always ( function () { this . setState ({ name : '' , gender : '' }); }. bind ( this ));

Every function expression above creates its own this scope. Without binding this we would not be able to call this.setState in the example above, because this would have been undefined.

Alternatively, we could have assigned this to a variable, e.g. var self = this and then used self.setState instead of this.setState inside the closures to get around this classic JavaScript problem.

In any case, here is an equivalent ES6 code using fat arrow functions which preserve the original this value:

$ . ajax ({ type : 'POST' , url : '/api/characters' , data : { name : name , gender : gender } }) . done (( data ) => { this . setState ({ helpBlock : data . message }); }) . fail (( jqXhr ) => { this . setState ({ helpBlock : jqXhr . responseJSON . message }); }) . always (() => { this . setState ({ name : '' , gender : '' }); });

Next, let’s talk about React, what makes it so special and why should we use it.

Step 5. React Crash Course

React is a JavaScript library for building web user interfaces. You could say it competes against AngularJS, Ember.js, Backbone and Polymer despite being much smaller in scope. React is just the V in the MVC (Model-View-Controller) architecture.

So, what is so special about React?

React components are written in a very declarative style. Unlike the “old way” using jQuery and such, you don’t interact with DOM directly. React manages all UI updates when the underlying data changes.

React is also very fast thanks to the Virtual DOM and diffing algorithm under the hood. When the data changes, React calculates the minimum number of DOM manipulations needed, then efficiently re-renders the component. For example, if there are 10,000 rendered items on a page and only 1 item changes, React will update just that DOM element, leaving 9,999 other items unchanged. That’s why React can get away with re-rendering the entire components without being ridiculously wasteful and slow.

Other notable features of React include:

Composability , i.e. make bigger, more complex components out of smaller components.

, i.e. make bigger, more complex components out of smaller components. Relatively easy to pick up since there isn’t that much to learn and it does not have a massive documentation like AngularJS and Ember.js.

since there isn’t that much to learn and it does not have a massive documentation like AngularJS and Ember.js. Server-side rendering allows us to easily build Isomorphic JavaScript apps.

allows us to easily build Isomorphic JavaScript apps. The most helpful error and warning messages that I have seen in any JavaScript library.

that I have seen in any JavaScript library. Components are self-contained; markup and behavior (and even styles) live in the same place, making components very reusable.

October 19, 2015 Update: We will not be building a true Isomorphic JavaScript app. If you disable JavaScript your Browser, a page will be rendered just fine for the most part, but it will not render any characters because that requires more work by fetching data from the database and then passing it to the root React component that will need to pass the data down to its children components, which is outside the scope of this tutorial.

I really like this excerpt from the React v0.14 Beta 1 blog post announcement that sums up nicely what React is all about:

It’s become clear that the beauty and essence of React has nothing to do with browsers or the DOM. We think the true foundations of React are simply ideas of components and elements: being able to describe what you want to render in a declarative way.

Before going any further please watch this awesome video React in 7 Minutes by John Lindquist.

And while you are there, I highly recommend getting the PRO subscription ($24.99/month) to unlock over 94 React and React Native video lessons. No, you will not become an expert just by watching these videos, but they are amazing at giving you short and straight to the point explanations on any particular topic. If you are on a budget, you can subscribe for 1 month, download all the videos, then cancel your subscription at the end of the month. Subscribing not only gives you access to React lessons, but also to TypeScript, Angular 2, D3, ECMAScript 6, Node.js and more.

Disclaimer: I am not affiliated with Egghead.io and I do not get any commissions for referrals.

While learning React, the biggest challenge for me was that it required a completely different thinking approach to building UIs. Which is why reading Thinking in React guide is absolutely a must for anyone who is starting out with React.

In similar fashion to the Product Table from Thinking in React, if we are to break apart the New Eden Faces UI into potential components, this is what it would look like:

Note Each component should try to adhere to the single responsibility principle. If you find yourself working on a component that does too many things, perhaps it's best to split it into sub-components. Having said that, I typically write monolithic components first, just to get it working, then refactor it by splitting it into smaller sub-components. Each component should try to adhere to the single responsibility principle. If you find yourself working on a component that does too many things, perhaps it's best to split it into sub-components. Having said that, I typically write monolithic components first, just to get it working, then refactor it by splitting it into smaller sub-components.

The top-level App component contains Navbar, Homepage and Footer components. Homepage component contains two Character components.

So, whenever you have a certain UI design in mind, start by breaking it apart from top-down and always be mindful of how your data propagates from parent to child, child to parent and between sibling components or you will quickly find yourself completely lost. It may be difficult initially, but it will become second nature to you after building a few React apps.

So, next time you decide to build a new app in React, before writing any code, do this hierarchy outline first. It will help you to visualize the relationships between multiple components and build them accordingly.

All components in React have a render() method. It always returns a single child element. Conversly, the following return statement is invalid because it contains 3 child elements:

render () { // Invalid JSX return ( < li > Achura < /li > < li > Civire < /li > < li > Deteis < /li > ); }

The HTML markup above is actually called JSX. As far syntax goes, it is just slightly different from HTML, for example className instead of class to define CSS classes. You will learn more about it as we start building the app.

When I first saw that syntax, I was immediately repulsed by it. I am used to returning booleans, numbers, strings, objects and functions in JavaScript, but certaintly not that. However, JSX is actually just a syntactic sugar. After fixing the code above by wrapping it with a <ul> tag (must return a single element), here is what it looks like without JSX:

render () { return React . createElement ( 'ul' , null , React . createElement ( 'li' , null , 'Achura' ), React . createElement ( 'li' , null , 'Civire' ), React . createElement ( 'li' , null , 'Deteis' ) ); }

I think you will agree that JSX is far more readable than plain JavaScript. Furthermore, Babel has a built-in support for JSX, so we don’t need to install anything extra. If you have ever worked with AngularJS directives then you will appreciate working with React components, so instead of having two different files — directive.js (logic) and template.html (presentation), you have a single file containing both logic and presentation.

The componentDidMount method in React is the closest thing to $(document).ready in jQuery. This method runs once (only on the client) immediately after initial rendering of the component. This is where you would typically initialize third-party libraries and jQuery plugins, or connect to Socket.IO.

You will be using Ternary operator in the render method quite a lot: hiding an element when data is empty, conditionally using CSS classes depending on some value, hiding or showing certain elements based on the component’s state and etc.

Consider the following example that conditionally sets the CSS class to text-danger or text-success based on the props value.

render () { let delta = this . props . delta ? ( < strong className = { this . props . delta > 0 ? 'text-success' : 'text-danger' } > { this . props . delta } < /strong > ) : null ; return ( < div className = 'card' > { delta } { this . props . title } < /div > ); }

We have only scratched the surface of everything there is to React, but this should be enough to give you a general idea about React as well as its benefits.

React on its own is actually really simple and easy to grasp. However, it is when we start talking about Flux architecture, things can get a little confusing.

Step 6. Flux Architecture Crash Course

Flux is the application architecture that was developed at Facebook for building scalable client-side web applications. It complements React’s components by utilizing a unidirectional data flow. Flux is more of a pattern than a framework, however, we will be using a Flux library called Alt to minimize writing the boilerplate code.

Have you seen this diagram before? Did it make any sense to you? It did not make any sense to me, no matter how many times I looked at it.

Now that I understand it better, I am actually really amazed by how such simple architecture can be presented in a such complicated way. But to Facebook’s credit, their new Flux diagrams are much better than before.

Fun Fact When I first began writing this tutorial I decided not to use Flux in this project. I could not grasp it for the life of me, let alone teach it to others. But thankfully, I get to work on cool stuff at Yahoo where I get to play and experiment with different technologies during my work hours. Honestly, we could have built this app without Flux and it would have been less lines of code. We don't have here any complex or nested components. But I believe that showing a full-stack React app with server-side rendering and Flux architecture, to see how all pieces connect together, has a value in of itself. When I first began writing this tutorial I decided not to use Flux in this project. I could not grasp it for the life of me, let alone teach it to others. But thankfully, I get to work on cool stuff at Yahoo where I get to play and experiment with different technologies during my work hours. Honestly, we could have built this app without Flux and it would have been less lines of code. We don't have here any complex or nested components. But I believe that showing a full-stack React app with server-side rendering and Flux architecture, to see how all pieces connect together, has a value in of itself.

Instead of reiterating the Flux Overview, let’s take a look at one of the real-world use cases in order to illustrate how Flux works:

1) On componentDidMount (when the page is rendered) three actions are fired:

OverviewActions . getSummary (); OverviewActions . getApps (); OverviewActions . getCompanies ();

2) Each one of those actions makes an AJAX request to the server to fetch the data.

3) When the data is fetched, each action fires another “success” action and passes the data along with it:

getSummary () { request . get ( '/api/overview/summary' ) . end (( err , res ) => { this . actions . getSummarySuccess ( res . body ); }); }

4) Meanwhile, the Overview store (a place where we keep the state for Overview component) is listening for those “success” actions. When the getSummarySuccess action is fired, onGetSummarySuccess method in the Overview store is called and the store is updated:

class OverviewStore { constructor () { this . bindActions ( OverviewActions ); this . summary = {}; this . apps = []; this . companies = []; } onGetSummarySuccess ( data ) { this . summary = data ; } onGetAppsSuccess ( data ) { this . apps = data ; } onGetCompaniesSuccess ( data ) { this . companies = data ; } }

5) As soon as the store is updated, the Overview component will know about it because it has subscribed to the Overview store. When a store is updated/changed, a component will set its own state to whatever is in that store.

class Overview extends React . Component { constructor ( props ) { super ( props ); this . state = OverviewStore . getState (); this . onChange = this . onChange . bind ( this ); } componentDidMount () { OverviewStore . listen ( this . onChange ); } onChange () { this . setState ( OverviewStore . getState ()) } ... }

6) At this point the Overview component has been updated with the new data.

7) In screenshot above,when the date range is updated from the dropdown menu, the entire process is repeated all over again.

Note Action names do not matter, use whatever naming convention you want as long as it is descriptive and makes sense. Action names do not matter, use whatever naming convention you want as long as it is descriptive and makes sense.

Ignoring the Dispatcher for a moment, can you see the one-way flow outlined above? If not, it’s alright, it will start making more sense as we start building the app.

Flux Summary

Flux is really just a fancy term for pub/sub architecture, i.e. data always flows one way through the application and it is picked up along the way by various subscribers (stores) who are listening to it.

There are more than a dozen of Flux implementations at the time of this writing. Out of them all, I only have experience with RefluxJS and Alt. Between the two, I personally prefer Alt for its simplicity, great support by @goatslacker, server-side rendering support, great documentation and the project is actively maintained.

I strongly encourage you to go through the Alt’s Getting Started guide. It will take no more than 10 minutes to skim through it.

If you are undecided on the Flux library, consider the following comment by glenjamin on Hacker News, in response to having a hard time figuring out which Flux library to use:

The dirty secret is: they’re probably all fine. It’s unlikely that you will choose a flux lib that will be the make or break point of your application. Even if a library stops being “maintained”, most decent flux incarnations are really small (~100 LoC) - it’s unlikely that there’s some fatal flaw you’ll be stuck with. In summary: redux is neat, but don’t beat yourself up over choosing the perfect flux lib - just grab one that you like the look of and move on with building your application.

Now that we have covered some basics behind ES6, React and Flux, it is time to move on to building the application.

Step 7. React Routes (Client-Side)

Create a new file App.js inside app/components with the following contents:

import React from 'react' ; class App extends React . Component { render () { return ( < div > { this . props . children } < /div > ); } } export default App ;

RouteHandler is a component that {this.props.children} now renders the active child route handler. It will render one of the following components depending on the URL path: Home, Top 100, Profile or Add Character.

October 19, 2015 Update: RouteHandler is gone. Router now automatically populates this.props.children of your components based on the active route.

Note It is similar to It is similar to <div ng-view></div> in AngularJS, which includes the rendered template of current route into the main layout.

Next, open routes.js inside app and paste the following:

import React from 'react' ; import { Route } from 'react-router' ; import App from './components/App' ; import Home from './components/Home' ; export default ( < Route component = { App } > < Route path = '/' component = { Home } / > < /Route > );

October 19, 2015 Update: The handler prop is now called component . Named routes are gone as well.

The reason for nesting routes this particular way is because we are going to place Navbar and Footer components, above and below the active route, inside the App component. Unlike other components, Navbar and Footer do not change/disappear between route transitions. (See outlined screenshot from Step 5)

Lastly, we need to add a URL listener and render the application when it changes. Open main.js inside the app directory that we created earlier and paste the following:

import React from 'react' ; import Router from 'react-router' ; import ReactDOM from 'react-dom' ; import createBrowserHistory from 'history/lib/createBrowserHistory' ; import routes from './routes' ; let history = createBrowserHistory (); ReactDOM . render ( < Router history = { history } > { routes } < /Router>, document.getElementById ( 'app' )) ;

October 19, 2015 Update: React.render now lives in the react-dom package. Router.HistoryLocation is now handled by the history package. We use history to enable HTML5 History API and to programmatically transition between routes. Routes are now passed in to the <Router> component as children instead of prop.

Note The main.js is the entry point for our React application. We use it in gulpfile.js where Browserify will traverse the entire tree of dependencies and generate the final bundle.js file. You will rarely have to touch this file after its initial setup. The main.js is the entry point for our React application. We use it in gulpfile.js where Browserify will traverse the entire tree of dependencies and generate the final bundle.js file. You will rarely have to touch this file after its initial setup.

React Router bootstraps the routes from routes.js file, matches them against a URL, and then executes the appropriate callback handler, which in this case means rendering a React component into <div id="app"></div> . But how does it know which component to render? Well, for example, if we are on / URL path, then {this.props.children} will render the Home component, because that’s what we have specified in routes.js. We will add more routes shortly.

Also, notice that we are using createBrowserHistory to enable HTML5 History API in order to make URLs look pretty. For example, it navigates to http://localhost:3000/add instead of http://localhost:3000/#add . Since we are building an Isomorphic React application (rendered on the server and the client) we do not have to do any hacky wildcard redirects on the server to enable this support. It just works out of the box.

Let’s create one last React component for this section. Create a new file Home.js inside app/components with the following contents:

import React from 'react' ; class Home extends React . Component { render () { return ( < div className = 'alert alert-info' > Hello from Home Component < /div > ); } } export default Home ;

Below should be everything we have created up to this point. This would be a good time to double check your code.

One last thing, open alt.js in the app directory and paste the following code. I will explain its purpose in Step 9 when we actually get to use it.

import Alt from 'alt' ; export default new Alt ();

Now we just need to set up a few more things on the back-end and then we can finally run the app.

Step 8. React Routes (Server-Side)

Open server.js and import the following modules by adding them at the top of the file:

// Babel ES6/JSX Compiler require ( 'babel-register' ); var swig = require ( 'swig' ); var React = require ( 'react' ); var ReactDOM = require ( 'react-dom/server' ); var Router = require ( 'react-router' ); var routes = require ( './app/routes' );

November 12, 2015 Update: Added Babel Require Hook. All subsequent files required by Node with the extensions .es6 , .es , .jsx and .js will be transformed by Babel. Since I have switched to Require Hook, it is no longer necessary to run the app using babel-node command as mentioned in Step 1. Furthermore, this Require Hook will use Babel presets we specified in package.json. To learn more about Require Hook usage and configuration, check out Babel documentation guide.

December 22, 2015 Update: Use require('babel-register') instead of require('babel-core/register') in accordance to Babel docs.

Next, add the following middleware to server.js, somewhere after existing Express middlewares:

app . use ( function ( req , res ) { Router . match ({ routes : routes . default , location : req . url }, function ( err , redirectLocation , renderProps ) { if ( err ) { res . status ( 500 ). send ( err . message ) } else if ( redirectLocation ) { res . status ( 302 ). redirect ( redirectLocation . pathname + redirectLocation . search ) } else if ( renderProps ) { var html = ReactDOM . renderToString ( React . createElement ( Router . RoutingContext , renderProps )); var page = swig . renderFile ( 'views/index.html' , { html : html }); res . status ( 200 ). send ( page ); } else { res . status ( 404 ). send ( 'Page Not Found' ) } }); });

October 19, 2015 Update: Previous React.renderToString now lives in the react-dom/server package. Additionally, server-side rendering with React Router has changed quite a bit. See Server Rendering Guide for more details.

November 12, 2015 Update: <RoutingContext {...renderProps} /> (ES6/JSX) has been replaced with React.createElement(Router.RoutingContext, renderProps) (ES5). That’s because Babel Require Hook transforms only subsequent files, not current file. In other words, using JSX here will result in an illegal syntax error. Of course there are ways around this limitation, but I wanted to avoid creating additional files.

Note: This screenshot is now outdated as of React 0.14 and React Router 1.0 but I left it here anyway to give you a better idea of where to place this middleware in server.js.

This middleware function will be executed on every request to the server, unless a request is handled by one the API endpoints that we will implement shortly.

Conditional statements within the Router.match should be self-explanatory. Depending on if we have 500 Error, 302 Redirect, 200 Success, 404 Not Found, we take different actions. The last two — 200 Success and 404 Not Found are usually the most common responses.

On the client-side, a rendered HTML markup gets inserted into <div id="app"></div> , while on the server a rendered HTML markup is sent to the index.html template where it is inserted into <div id="app">{{ html|safe }}</div> by the Swig template engine. I chose Swig because I wanted to try something other than Jade and Handlebars this time.

But do we really need a separate template for this? Why not just render everything inside the App component? Yes, you could do it, as long as you are okay with invalid HTML markup and not being able to include inline script tags like Google Analytics directly in the App component. But having said that, invalid markup is probably not relevant to SEO anymore and there are workarounds to include inline script tags in React components. So it’s up to you, but for the purposes of this tutorial we will be using a Swig template.

One last thing I need to explain are those JavaScript tripple dots. It is called the ES6 spread operator used in {...renderProps} above. That’s basically like saying “just pass me everything”. Since renderProps contains multiple things - routes, params, components, location, history, it would be a hassle to pass them as individual props. Spread operator is a handy shortcut for situations like these.

October 19, 2015 Update: Added a new paragraph about the spread operator and new Router matching conditions.

November 12, 2015 Update: Spread operator is no longer used, however, previous paragraph is still relevant and useful, since you will no doubt run into this ... notation again when developing React apps.

Create a new folder views in the project root directory (next to package.json and server.js). Then inside views, create a new file index.html:

<!DOCTYPE html> <html> <head> <meta charset= "utf-8" /> <meta http-equiv= "X-UA-Compatible" content= "IE=edge" /> <meta name= "viewport" content= "width=device-width, initial-scale=1" /> <title> New Eden Faces </title> <link rel= "stylesheet" href= "https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,900" /> <link rel= "stylesheet" href= "/css/main.css" /> </head> <body> <div id= "app" > {{ html|safe }} </div> <script src= "/js/vendor.js" ></script> <script src= "/js/vendor.bundle.js" ></script> <script src= "/js/bundle.js" ></script> </body> </html>

Open two Terminal tabs. In one tab run gulp to build the app, concatenate vendor files, compile LESS stylesheets and watch for file changes:

In another tab, run npm run watch to start the Node.js server and automatically restart the process on file changes:

July 27, 2015 Update: Once again, make sure you have installed nodemon via sudo npm install -g nodemon otherwise you will not be able to run the command above.

Open http://localhost:3000 and you should see our React app render successfully:

We did an impressive amount of work just to display an empty page with a simple alert message! Fortunately, the most difficult part is behind us. From here on we can relax and focus on building React components and implementing the REST API endpoints.

Both gulp and npm run watch processes will take care of everything for us. We no longer need to worry about re-compiling the app after adding new React components or restarting the Express app after making changes to server.js.

Both Navbar and Footer are relatively simple components. The Footer component fetches and displays the Top 5 characters. The Navbar component fetches and displays the total character count and initializes a Socket.IO event listener for tracking the number of online visitors.

Note This section will be slightly longer than the rest since I will be covering a lot of new concepts that other sections are built upon. This section will be slightly longer than the rest since I will be covering a lot of new concepts that other sections are built upon.

Component

Create a new file Footer.js inside components directory:

import React from 'react' ; import { Link } from 'react-router' ; import FooterStore from '../stores/FooterStore' import FooterActions from '../actions/FooterActions' ; class Footer extends React . Component { constructor ( props ) { super ( props ); this . state = FooterStore . getState (); this . onChange = this . onChange . bind ( this ); } componentDidMount () { FooterStore . listen ( this . onChange ); FooterActions . getTopCharacters (); } componentWillUnmount () { FooterStore . unlisten ( this . onChange ); } onChange ( state ) { this . setState ( state ); } render () { let leaderboardCharacters = this . state . characters . map (( character ) => { return ( < li key = { character . characterId } > < Link to = { '/characters/' + character . characterId } > < img className = 'thumb-md' src = { 'http://image.eveonline.com/Character/' + character . characterId + '_128.jpg' } / > < /Link > < /li > ) }); return ( < footer > < div className = 'container' > < div className = 'row' > < div className = 'col-sm-5' > < h3 className = 'lead' >< strong > Information < /strong> and <strong>Copyright</ strong >< /h3 > < p > Powered by < strong > Node . js < /strong>, <strong>MongoDB</ strong > and < strong > React < /strong> with Flux architecture and server-side rendering.</ p > < p > You may view the < a href = 'https://github.com/sahat/newedenfaces-react' > Source Code < /a> behind this project on GitHub.</ p > < p > © 2015 Sahat Yalkabov . < /p > < /div > < div className = 'col-sm-7 hidden-xs' > < h3 className = 'lead' >< strong > Leaderboard < /strong> Top 5 Characters</ h3 > < ul className = 'list-inline' > { leaderboardCharacters } < /ul > < /div > < /div > < /div > < /footer > ); } } export default Footer ;

Just this once, I will show an ES5-equivalent code for this component in case you are still not comfortable with the new ES6 syntax. Also, see Using Alt with ES5 guide for syntax differences when creating actions and creating stores.

var React = require ( 'react' ); var Link = require ( 'react-router' ). Link ; var FooterStore = require ( '../stores/FooterStore' ); var FooterActions = require ( '../actions/FooterActions' ); var Footer = React . createClass ({ getInitialState : function () { return FooterStore . getState (); } componentDidMount : function () { FooterStore . listen ( this . onChange ); FooterActions . getTopCharacters (); } componentWillUnmount : function () { FooterStore . unlisten ( this . onChange ); } onChange : function ( state ) { this . setState ( state ); } render () { var leaderboardCharacters = this . state . characters . map ( function ( character ) { return ( < li key = { character . characterId } > < Link to = { '/characters/' + character . characterId } > < img className = 'thumb-md' src = { 'http://image.eveonline.com/Character/' + character . characterId + '_128.jpg' } / > < /Link > < /li > ); }); return ( < footer > < div className = 'container' > < div className = 'row' > < div className = 'col-sm-5' > < h3 className = 'lead' >< strong > Information < /strong> and <strong>Copyright</ strong >< /h3 > < p > Powered by < strong > Node . js < /strong>, <strong>MongoDB</ strong > and < strong > React < /strong> with Flux architecture and server-side rendering.</ p > < p > You may view the < a href = 'https://github.com/sahat/newedenfaces-react' > Source Code < /a> behind this project on GitHub.</ p > < p > © 2015 Sahat Yalkabov . < /p > < /div > < div className = 'col-sm-7 hidden-xs' > < h3 className = 'lead' >< strong > Leaderboard < /strong> Top 5 Characters</ h3 > < ul className = 'list-inline' > { leaderboardCharacters } < /ul > < /div > < /div > < /div > < /footer > ); } }); module . exports = Footer ;

If you can recall the previous section on Flux architecture, then this should all be familiar to you. When component is loaded it sets the initial component state to whatever is in the FooterStore and initialzes a store listener, likewise when component is unloaded (e.g. navigated to a different page) that store listener is removed. When the store is updated, onChange function is called, which in turn updates the Footer’s state.

If by any chance you have used React before, there is something you need to keep in mind when creating React components using ES6 classes. Component methods no longer autobind this context. For example, when calling an internal component method that uses this , you need to bind this explicitly. Previously, React.createClass() was doing it for us internally:

Autobinding: When creating callbacks in JavaScript, you usually need to explicitly bind a method to its instance such that the value of this is correct. With React, every method is automatically bound to its component instance.

That is why we have the following line in ES6, but not in ES5:

this . onChange = this . onChange . bind ( this );

Here is a more complete example on this issue:

class App extends React . Component { constructor ( props ) { super ( props ); this . state = AppStore . getState (); this . onChange = this . onChange ; // Need to add `.bind(this)`. } onChange ( state ) { // Object `this` will be undefined without binding it explicitly. this . setState ( state ); } render () { return null ; } }

You may or may not be familiar with the map() method in JavaScript. Even if you have used it before, it may still be unclear how it works in the context of JSX. (Something that React Tutorial regretfully does not explain very well.)

It is basically a for-each loop, similar to what you might see in Jade and Handlebars, but here you can assign the results to a variable, which can then be used with JSX by wrapping it in curly braces. It’s a very common pattern in React so you will be using it quite frequently.

Note When rendering leaderboardCharacters above, React requires that you use the key property to uniquely identify each child element. When rendering dynamic children , such asabove, React requires that you use theproperty to uniquely identify each child element.

A Link component will render a fully accesible anchor tag with the proper href. It also knows when the route it links to is active and automatically applies its “active” CSS class. If you are using React Router, then you need to be using Link for internal navigation between routes.

Actions

Next, we are going to create actions and a store for the Footer component. Create a new file called FooterActions.js in app/actions directory:

import alt from '../alt' ; class FooterActions { constructor () { this . generateActions ( 'getTopCharactersSuccess' , 'getTopCharactersFail' ); } getTopCharacters () { $ . ajax ({ url : '/api/characters/top' }) . done (( data ) => { this . actions . getTopCharactersSuccess ( data ) }) . fail (( jqXhr ) => { this . actions . getTopCharactersFail ( jqXhr ) }); } } export default alt . createActions ( FooterActions );

First, notice that we import an instance of Alt (alt.js from Step 7), not the Alt module installed in node_modules. It is an instance of Alt which instantiates Flux dispatcher and provides methods for creating Alt actions and stores. You can think of it as a glue between all of our stores and actions.

We have three actions here - the one that fetches the data using jQuery.ajax() and two that notify the store whether that action was successful or unsuccessful. In this particular case, it is not very useful to know when getTopCharacters action is fired. What we really want to know is if that action was successful (update the store, then re-render the component with new data) or unsuccessful (display an error notification).

Actions can be as complex or as simple as you need them to be. Some actions are “actions” themselves, where we don’t care what they do or what they send, the fact that action was fired is all we need to know. For example, ajaxInProgress and ajaxComplete to notify a store when AJAX request is in progress or complete.

Note Alt actions can be created via a shorthand notation using generateActions method. From the documentation on Alt actions can be created via a shorthand notation usingmethod. From the documentation on Creating Actions — If all of your actions are just straight through dispatches you can shorthand generate them using this function.

The two shorthand actions above created via generateActions and the following two simple actions are equivalent, so use either notation based on your preference:

getTopCharactersSuccess ( payload ) { this . dispatch ( payload ); } getTopCharactersFail ( payload ) { this . dispatch ( payload ); } // Equivalent to this... this . generateActions ( 'getTopCharactersSuccess' , 'getTopCharactersFail' );

And lastly, we wrap the FooterActions class with alt.createActions and then export it, so that we could import and use it in the Footer component.

Store

Next, create a new file called FooterStore.js inside app/stores directory:

import alt from '../alt' ; import FooterActions from '../actions/FooterActions' ; class FooterStore { constructor () { this . bindActions ( FooterActions ); this . characters = []; } onGetTopCharactersSuccess ( data ) { this . characters = data . slice ( 0 , 5 ); } onGetTopCharactersFail ( jqXhr ) { // Handle multiple response formats, fallback to HTTP status code number. toastr . error ( jqXhr . responseJSON && jqXhr . responseJSON . message || jqXhr . responseText || jqXhr . statusText ); } } export default alt . createStore ( FooterStore );

All instance variables of the store, i.e. values assigned to this , will become part of the state. When Footer component initially calls FooterStore.getState() it receives the current state of the store specified in the constructor (initially just an empty array, and mapping over an empty array returns another empty array, hence nothing is rendered when the Footer component is first loaded).

bindActions is a magic Alt method which binds actions to their handlers defined in the store. For example, an action with the name foo will match an action handler method defined in the store named onFoo or just foo but not both. That is why for actions getTopCharactersSuccess and getTopCharactersFail defined in FooterActions.js we have corresponding store handlers called onGetTopCharactersSuccess and onGetTopCharactersFail in FooterStore.js.

Note For more precise control over which actions the store listens to and what handlers those actions are bound to, see For more precise control over which actions the store listens to and what handlers those actions are bound to, see bindListeners method.

I hope it’s pretty clear by now that when getTopCharactersSuccess action is fired, onGetTopCharactersSuccess handler function is executed and the store is updated with the new data that contains Top 5 Characters. And since we have initialized the store listener in the Footer component, it will be notified when the FooterStore has been updated and the component will re-render accordingly.

We will be using Toastr JavaScript library for notifications. Why not just use pure React notification component you may ask? While you may find some notification components built specifically for React, I personally think it is one of the few areas that should not be handled by React (along with tooltips). I think it is far easier to display a notification imperatively from any part of your application than having to declaratively render notification component based on the current state. I have built a notification component with React and Flux before, but frankly it was a big pain dealing with hide/show states, animation and z-index positioning.

Open App.js inside app/components and import the Footer component:

import Footer from './Footer' ;

Then add <Footer /> right after the {this.props.children} line:

<div> {this.props.children} <Footer /> </div>

Refresh the browser and you should see the new footer.

We will implement Express API endpoints and populate the database with characters shortly, but for now let’s continue on to the Navbar component. Since I have already covered the basics behind Alt actions and stores, and how they fit in with our app architecture, this will be a shorter sub-section.

Component

Create a new file Navbar.js inside app/components directory:

import React from 'react' ; import { Link } from 'react-router' ; import NavbarStore from '../stores/NavbarStore' ; import NavbarActions from '../actions/NavbarActions' ; class Navbar extends React . Component { constructor ( props ) { super ( props ); this . state = NavbarStore . getState (); this . onChange = this . onChange . bind ( this ); } componentDidMount () { NavbarStore . listen ( this . onChange ); NavbarActions . getCharacterCount (); let socket = io . connect (); socket . on ( 'onlineUsers' , ( data ) => { NavbarActions . updateOnlineUsers ( data ); }); $ ( document ). ajaxStart (() => { NavbarActions . updateAjaxAnimation ( 'fadeIn' ); }); $ ( document ). ajaxComplete (() => { setTimeout (() => { NavbarActions . updateAjaxAnimation ( 'fadeOut' ); }, 750 ); }); } componentWillUnmount () { NavbarStore . unlisten ( this . onChange ); } onChange ( state ) { this . setState ( state ); } handleSubmit ( event ) { event . preventDefault (); let searchQuery = this . state . searchQuery . trim (); if ( searchQuery ) { NavbarActions . findCharacter ({ searchQuery : searchQuery , searchForm : this . refs . searchForm , history : this . props . history }); } } render () { return ( < nav className = 'navbar navbar-default navbar-static-top' > < div className = 'navbar-header' > < button type = 'button' className = 'navbar-toggle collapsed' data - toggle = 'collapse' data - target = '#navbar' > < span className = 'sr-only' > Toggle navigation < /span > < span className = 'icon-bar' >< /span > < span className = 'icon-bar' >< /span > < span className = 'icon-bar' >< /span > < /button > < Link to = '/' className = 'navbar-brand' > < span ref = 'triangles' className = { 'triangles animated ' + this . state . ajaxAnimationClass } > < div className = 'tri invert' >< /div > < div className = 'tri invert' >< /div > < div className = 'tri' >< /div > < div className = 'tri invert' >< /div > < div className = 'tri invert' >< /div > < div className = 'tri' >< /div > < div className = 'tri invert' >< /div > < div className = 'tri' >< /div > < div className = 'tri invert' >< /div > < /span > NEF < span className = 'badge badge-up badge-danger' > { this . state . onlineUsers } < /span > < /Link > < /div > < div id = 'navbar' className = 'navbar-collapse collapse' > < form ref = 'searchForm' className = 'navbar-form navbar-left animated' onSubmit = { this . handleSubmit . bind ( this )} > < div className = 'input-group' > < input type = 'text' className = 'form-control' placeholder = { this . state . totalCharacters + ' characters' } value = { this . state . searchQuery } onChange = { NavbarActions . updateSearchQuery } / > < span className = 'input-group-btn' > < button className = 'btn btn-default' onClick = { this . handleSubmit . bind ( this )} >< span className = 'glyphicon glyphicon-search' >< /span></ button > < /span > < /div > < /form > < ul className = 'nav navbar-nav' > < li >< Link to = '/' > Home < /Link></ li > < li >< Link to = '/stats' > Stats < /Link></ li > < li className = 'dropdown' > < a href = '#' className = 'dropdown-toggle' data - toggle = 'dropdown' > Top 100 < span className = 'caret' >< /span></ a > < ul className = 'dropdown-menu' > < li >< Link to = '/top' > Top Overall < /Link></ li > < li className = 'dropdown-submenu' > < Link to = '/top/caldari' > Caldari < /Link > < ul className = 'dropdown-menu' > < li >< Link to = '/top/caldari/achura' > Achura < /Link></ li > < li >< Link to = '/top/caldari/civire' > Civire < /Link></ li > < li >< Link to = '/top/caldari/deteis' > Deteis < /Link></ li > < /ul > < /li > < li className = 'dropdown-submenu' > < Link to = '/top/gallente' > Gallente < /Link > < ul className = 'dropdown-menu' > < li >< Link to = '/top/gallente/gallente' > Gallente < /Link></ li > < li >< Link to = '/top/gallente/intaki' > Intaki < /Link></ li > < li >< Link to = '/top/gallente/jin-mei' > Jin - Mei < /Link></ li > < /ul > < /li > < li className = 'dropdown-submenu' > < Link to = '/top/minmatar' > Minmatar < /Link > < ul className = 'dropdown-menu' > < li >< Link to = '/top/minmatar/brutor' > Brutor < /Link></ li > < li >< Link to = '/top/minmatar/sebiestor' > Sebiestor < /Link></ li > < li >< Link to = '/top/minmatar/vherokior' > Vherokior < /Link></ li > < /ul > < /li > < li className = 'dropdown-submenu' > < Link to = '/top/amarr' > Amarr < /Link > < ul className = 'dropdown-menu' > < li >< Link to = '/top/amarr/amarr' > Amarr < /Link></ li > < li >< Link to = '/top/amarr/ni-kunni' > Ni - Kunni < /Link></ li > < li >< Link to = '/top/amarr/khanid' > Khanid < /Link></ li > < /ul > < /li > < li className = 'divider' >< /li > < li >< Link to = '/shame' > Hall of Shame < /Link></ li > < /ul > < /li > < li className = 'dropdown' > < a href = '#' className = 'dropdown-toggle' data - toggle = 'dropdown' > Female < span className = 'caret' >< /span></ a > < ul className = 'dropdown-menu' > < li >< Link to = '/female' > All < /Link></ li > < li className = 'dropdown-submenu' > < Link to = '/female/caldari' > Caldari < /Link > < ul className = 'dropdown-menu' > < li >< Link to = '/female/caldari/achura' > Achura < /Link></ li > < li >< Link to = '/female/caldari/civire/' > Civire < /Link></ li > < li >< Link to = '/female/caldari/deteis' > Deteis < /Link></ li > < /ul > < /li > < li className = 'dropdown-submenu' > < Link to = '/female/gallente' > Gallente < /Link > < ul className = 'dropdown-menu' > < li >< Link to = '/female/gallente/gallente' > Gallente < /Link></ li > < li >< Link to = '/female/gallente/intaki' > Intaki < /Link></ li > < li >< Link to = '/female/gallente/jin-mei' > Jin - Mei < /Link></ li > < /ul > < /li > < li className = 'dropdown-submenu' > < Link to = '/female/minmatar' > Minmatar < /Link > < ul className = 'dropdown-menu' > < li >< Link to = '/female/minmatar/brutor' > Brutor < /Link></ li > < li >< Link to = '/female/minmatar/sebiestor' > Sebiestor < /Link></ li > < li >< Link to = '/female/minmatar/vherokior' > Vherokior < /Link></ li > < /ul > < /li > < li className = 'dropdown-submenu' > < Link to = '/female/amarr' > Amarr < /Link > < ul className = 'dropdown-menu' > < li >< Link to = '/female/amarr/amarr' > Amarr < /Link></ li > < li >< Link to = '/female/amarr/ni-kunni' > Ni - Kunni < /Link></ li > < li >< Link to = '/female/amarr/khanid' > Khanid < /Link></ li > < /ul > < /li > < /ul > < /li > < li className = 'dropdown' > < a href = '#' className = 'dropdown-toggle' data - toggle = 'dropdown' > Male < span className = 'caret' >< /span></ a > < ul className = 'dropdown-menu' > < li >< Link to = '/male' > All < /Link></ li > < li className = 'dropdown-submenu' > < Link to = '/male/caldari' > Caldari < /Link > < ul className = 'dropdown-menu' > < li >< Link to = '/male/caldari/achura' > Achura < /Link></ li > < li >< Link to = '/male/caldari/civire' > Civire < /Link></ li > < li >< Link to = '/male/caldari/deteis' > Deteis < /Link></ li > < /ul > < /li > < li className = 'dropdown-submenu' > < Link to = '/male/gallente' > Gallente < /Link > < ul className = 'dropdown-menu' > < li >< Link to = '/male/gallente/gallente' > Gallente < /Link></ li > < li >< Link to = '/male/gallente/intaki' > Intaki < /Link></ li > < li >< Link to = '/male/gallente/jin-mei' > Jin - Mei < /Link></ li > < /ul > < /li > < li className = 'dropdown-submenu' > < Link to = '/male/minmatar' > Minmatar < /Link > < ul className = 'dropdown-menu' > < li >< Link to = '/male/minmatar/brutor' > Brutor < /Link></ li > < li >< Link to = '/male/minmatar/sebiestor' > Sebiestor < /Link></ li > < li >< Link to = '/male/minmatar/vherokior' > Vherokior < /Link></ li > < /ul > < /li > < li className = 'dropdown-submenu' > < Link to = '/male/amarr' > Amarr < /Link > < ul className = 'dropdown-menu' > < li >< Link to = '/male/amarr/amarr' > Amarr < /Link></ li > < li >< Link to = '/male/amarr/ni-kunni' > Ni - Kunni < /Link></ li > < li >< Link to = '/male/amarr/khanid' > Khanid < /Link></ li > < /ul > < /li > < /ul > < /li > < li >< Link to = '/add' > Add < /Link></ li > < /ul > < /div > < /nav > ); } } export default Navbar ;

October 19, 2015 Update: Removed Navbar.contextTypes that was previously used to get an instance of the router and removed getDOMNode() method call since this.refs.searchForm already returns a DOM node now.

Yes it is certainly possible to write most of the above markup dynamically with less lines of code by iterating through all races, then through all bloodlines, however, this was one of those things that I copy & pasted from my original project and didn’t want to focus on too much.

One thing you will probably notice right away is the class variable contextTypes . We need it for referencing an instance of the router, which in turn gives us access to current path, current query parameters, route parameters and transitions to other routes. Now the history object (navigation) will be passed as a prop from the App component. We actually do not use it directly in the Navbar component but instead pass it as an argument to Navbar actions so that it could navigate to a particular character profile page from the Navbar store, after successfully fetching data from the server. We obviously cannot navigate from within the component since no action has been fired yet and no character data has been received. There are certainly other ways to get history or router object references inside a Flux store, but this is the least complicated solution I could think of.

componentDidMount is where we establish connection with Socket.IO and initialize ajaxStart and ajaxComplete event listeners used for fading in/out the loading indicator on AJAX requests, which is located next to the NEF logo.

handleSubmit is a form submit handler that gets executed by pressing the Enter key or clicking the (Search) button. It essentially does some input cleanup and validation, then fires the findCharacter action. In addition to the search query and the router instance, we also pass a reference to the search field DOM Node so that we could display a shaking animation when a character name is not found.

Actions

Let’s create a new file NavbarActions.js in the app/actions directory:

import alt from '../alt' ; import { assign } from 'underscore' ; class NavbarActions { constructor () { this . generateActions ( 'updateOnlineUsers' , 'updateAjaxAnimation' , 'updateSearchQuery' , 'getCharacterCountSuccess' , 'getCharacterCountFail' , 'findCharacterSuccess' , 'findCharacterFail' ); } findCharacter ( payload ) { $ . ajax ({ url : '/api/characters/search' , data : { name : payload . searchQuery } }) . done (( data ) => { assign ( payload , data ); this . actions . findCharacterSuccess ( payload ); }) . fail (() => { this . actions . findCharacterFail ( payload ); }); } getCharacterCount () { $ . ajax ({ url : '/api/characters/count' }) . done (( data ) => { this . actions . getCharacterCountSuccess ( data ) }) . fail (( jqXhr ) => { this . actions . getCharacterCountFail ( jqXhr ) }); } } export default alt . createActions ( NavbarActions );

Most of these actions should be pretty self-explanatory, but if it is unclear, see brief descriptions below.

Action Description updateOnlineUsers Sets online users count on Socket.IO event update. updateAjaxAnimation Adds “fadeIn” or “fadeOut” CSS class to the loading indicator. updateSearchQuery Update search query value on keypress. getCharacterCount Fetch total number of characters from the server. getCharacterCountSuccess Returns total number of characters. getCharacterCountFail Returns jQuery jqXhr object. findCharacter Find a character by name.

The reason why we add the shake CSS class and then remove it one second later is so that we could repeat this animation, otherwise if we just keep on adding the shake it will not animate again.

Store

Create a new file NavbarStore.js in the app/stores directory:

import alt from '../alt' ; import NavbarActions from '../actions/NavbarActions' ; class NavbarStore { constructor () { this . bindActions ( NavbarActions ); this . totalCharacters = 0 ; this . onlineUsers = 0 ; this . searchQuery = '' ; this . ajaxAnimationClass = '' ; } onFindCharacterSuccess ( payload ) { payload . history . pushState ( null , '/characters/' + payload . characterId ); } onFindCharacterFail ( payload ) { payload . searchForm . classList . add ( 'shake' ); setTimeout (() => { payload . searchForm . classList . remove ( 'shake' ); }, 1000 ); } onUpdateOnlineUsers ( data ) { this . onlineUsers = data . onlineUsers ; } onUpdateAjaxAnimation ( className ) { this . ajaxAnimationClass = className ; //fadein or fadeout } onUpdateSearchQuery ( event ) { this . searchQuery = event . target . value ; } onGetCharacterCountSuccess ( data ) { this . totalCharacters = data . count ; } onGetCharacterCountFail ( jqXhr ) { toastr . error ( jqXhr . responseJSON . message ); } } export default alt . createStore ( NavbarStore );

October 19, 2015 Update: Changed router.transitionTo to history.pushState for page navigation.

Recall this line in the Navbar component that we created above:

<input type= 'text' className= 'form-control' placeholder= {this.state.totalCharacters + ' characters '} value= {this.state.searchQuery} onChange= {NavbarActions.updateSearchQuery} />

Since onChange handler returns and event object, we are using event.target.value to get the text field value inside onUpdateSearchQuery function.

Open App.js again and import the Navbar component:

import Navbar from './Navbar' ;

Then add <Navbar /> right before the this.props.children component:

<div> <Navbar history= {this.props.history} /> {this.props.children} <Footer /> </div>

October 19, 2015 Update: If you recall, we created a history object via createBrowserHistory inside main.js and passed it as a prop to the <Router> . That’s why this prop is available in the App.js component. Here, we are just passing it even further down to the Navbar component.

Since we haven’t yet configured Socket.IO on the server or implemented any of the API routes, you will not see the total number of online visitors (red circle next to the logo) or total characters (search placeholder text).

Step 10. Socket.IO - Real-time User Count

Unlike the previous section, this one will be fairly short and focused specifically on the server-side aspect of Socket.IO.

Open server.js and find the following line:

app . listen ( app . get ( 'port' ), function () { console . log ( 'Express server listening on port ' + app . get ( 'port' )); });

Then replace it with the following code:

/** * Socket.io stuff. */ var server = require ( 'http' ). createServer ( app ); var io = require ( 'socket.io' )( server ); var onlineUsers = 0 ; io . sockets . on ( 'connection' , function ( socket ) { onlineUsers ++ ; io . sockets . emit ( 'onlineUsers' , { onlineUsers : onlineUsers }); socket . on ( 'disconnect' , function () { onlineUsers -- ; io . sockets . emit ( 'onlineUsers' , { onlineUsers : onlineUsers }); }); }); server . listen ( app . get ( 'port' ), function () { console . log ( 'Express server listening on port ' + app . get ( 'port' )); });

In a nutshell, when a WebSocket connection is established, it increments the onlineUsers count (global variable) and broadcasts a message — “Hey, I have this many online visitors now.”. When someone closes the browser and leaves, the onlineUsers count is decremented and it yet again broadcasts a message “Hey, someone just left, I have this many online visitors now.”.

Note If you have never used Socket.IO then If you have never used Socket.IO then Chat application tutorial is a great starting point.

Open index.html in the views directory and add the following line right next with other scripts:

<script src= "/socket.io/socket.io.js" ></script>

Refresh the browser and open http://localhost:3000 in multiple tabs to simulate multiple user connections. You should now see the total number of visitors in the red circle next to the logo.

At this point we are neither finished with the front-end nor do we have any working API endpoints. We could have focused on building just the front-end in the first half of the tutorial and then the back-end in the second half of the tutorial, or vice versa, but personally, I have never built an app like that. I typically go back and forth between back-end and front-end parts of the application during my development flow.

We can’t display any characters until they are added to the database. In order to add new characters to the database we need to build a UI for it and implement an API endpoint. That’s exactly what we will do next.

Step 11. Add Character Component

This component consists of a simple form with a text field, radio buttons and a submit button. Success and error messages will be displayed within help-block under the text field.

Component

Create a new file AddCharacter.js in app/components directory:

import React from 'react' ; import AddCharacterStore from '../stores/AddCharacterStore' ; import AddCharacterActions from '../actions/AddCharacterActions' ; class AddCharacter extends React . Component { constructor ( props ) { super ( props ); this . state = AddCharacterStore . getState (); this . onChange = this . onChange . bind ( this ); } componentDidMount () { AddCharacterStore . listen ( this . onChange ); } componentWillUnmount () { AddCharacterStore . unlisten ( this . onChange ); } onChange ( state ) { this . setState ( state ); } handleSubmit ( event ) { event . preventDefault (); var name = this . state . name . trim (); var gender = this . state . gender ; if ( ! name ) { AddCharacterActions . invalidName (); this . refs . nameTextField . getDOMNode (). focus (); } if ( ! gender ) { AddCharacterActions . invalidGender (); } if ( name && gender ) { AddCharacterActions . addCharacter ( name , gender ); } } render () { return ( < div className = 'container' > < div className = 'row flipInX animated' > < div className = 'col-sm-8' > < div className = 'panel panel-default' > < div className = 'panel-heading' > Add Character < /div > < div className = 'panel-body' > < form onSubmit = { this . handleSubmit . bind ( this )} > < div className = { 'form-group ' + this . state . nameValidationState } > < label className = 'control-label' > Character Name < /label > < input type = 'text' className = 'form-control' ref = 'nameTextField' value = { this . state . name } onChange = { AddCharacterActions . updateName } autoFocus /> < span className = 'help-block' > { this . state . helpBlock } < /span > < /div > < div className = { 'form-group ' + this . state . genderValidationState } > < div className = 'radio radio-inline' > < input type = 'radio' name = 'gender' id = 'female' value = 'Female' checked = { this . state . gender === 'Female' } onChange = { AddCharacterActions . updateGender } / > < label htmlFor = 'female' > Female < /label > < /div > < div className = 'radio radio-inline' > < input type = 'radio' name = 'gender' id = 'male' value = 'Male' checked = { this . state . gender === 'Male' } onChange = { AddCharacterActions . updateGender } / > < label htmlFor = 'male' > Male < /label > < /div > < /div > < button type = 'submit' className = 'btn btn-primary' > Submit < /button > < /form > < /div > < /div > < /div > < /div > < /div > ); } } export default AddCharacter ;

You should start to see by now what all these components have in common:

Set the initial component state to what’s in the store. Add a store listener in componentDidMount , remove it in componentWillUnmount . Add onChange method which updates component’s state whenever the store is updated.

handleSubmit does exactly what you might think — handles the form submission for adding a new character. While it is true that we could have done form validation inside addCharacter action instead, however, doing so would also require us to pass the text field DOM reference, because when nameTextField is invalid, it needs to be “focused” so that a user can start typing again without having to click the text field.

Actions

Create a new file AddCharacterActions.js in app/actions directory:

import alt from '../alt' ; class AddCharacterActions { constructor () { this . generateActions ( 'addCharacterSuccess' , 'addCharacterFail' , 'updateName' , 'updateGender' , 'invalidName' , 'invalidGender' ); } addCharacter ( name , gender ) { $ . ajax ({ type : 'POST' , url : '/api/characters' , data : { name : name , gender : gender } }) . done (( data ) => { this . actions . addCharacterSuccess ( data . message ); }) . fail (( jqXhr ) => { this . actions . addCharacterFail ( jqXhr . responseJSON . message ); }); } } export default alt . createActions ( AddCharacterActions );

We are firing addCharacterSuccess action when character has been added to the database successfully and addCharacterFail when character could not be added, perhaps due to an invalid name or because it already exists in the database. Both updateName and updateGender actions are fired when the Character Name text field and Gender radio button is updated via onChange , respectively. And likewise, invalidName and invalidGender actions are fired when you a user submits the form without entering a name or selecting a gender.

Store

Create a new file AddCharacterStore.js in app/stores directory:

import alt from '../alt' ; import AddCharacterActions from '../actions/AddCharacterActions' ; class AddCharacterStore { constructor () { this . bindActions ( AddCharacterActions ); this . name = '' ; this . gender = '' ; this . helpBlock = '' ; this . nameValidationState = '' ; this . genderValidationState = '' ; } onAddCharacterSuccess ( successMessage ) { this . nameValidationState = 'has-success' ; this . helpBlock = successMessage ; } onAddCharacterFail ( errorMessage ) { this . nameValidationState = 'has-error' ; this . helpBlock = errorMessage ; } onUpdateName ( event ) { this . name = event . target . value ; this . nameValidationState = '' ; this . helpBlock = '' ; } onUpdateGender ( event ) { this . gender = event . target . value ; this . genderValidationState = '' ; } onInvalidName () { this . nameValidationState = 'has-error' ; this . helpBlock = 'Please enter a character name.' ; } onInvalidGender () { this . genderValidationState = 'has-error' ; } } export default alt . createStore ( AddCharacterStore );

nameValidationState and genderValidationState refers to the validation states on form controls provided by Bootstrap.

helpBlock is a status message which gets displayed below the text field, e.g. Character has been added successfully.

onInvalidName handler is fired when Character Name field is empty. If the name does not exist in EVE Online database it will be a different error message provided by onAddCharacterFail handler.

Finally, open routes.js and add a new route /add with the AddCharacter component handler:

import React from 'react' ; import { Route } from 'react-router' ; import App from './components/App' ; import Home from './components/Home' ; import AddCharacter from './components/AddCharacter' ; export default ( < Route component = { App } > < Route path = '/' component = { Home } / > < Route path = '/add' component = { AddCharacter } / > < /Route > );

Here is a quick demonstration of the entire flow from the moment you start typing a character’s name:

Fire updateName action, passing the event object as its payload. Call onUpdateName store handler. Update the state with the new name.

In the next few sections we will implement the back-end code for adding and saving new characters to the database.

Step 12. Database Schema

In the top-level directory (next to package.json and server.js files) create a new folder models, then inside create a new file character.js and paste the following:

var mongoose = require ( 'mongoose' ); var characterSchema = new mongoose . Schema ({ characterId : { type : String , unique : true , index : true }, name : String , race : String , gender : String , bloodline : String , wins : { type : Number , default : 0 }, losses : { type : Number , default : 0 }, reports : { type : Number , default : 0 }, random : { type : [ Number ], index : '2d' }, voted : { type : Boolean , default : false } }); module . exports = mongoose . model ( 'Character' , characterSchema );

A schema is just a representation of your data in MongoDB. This is where you can enforce a certain field to be of particular type. A field can also be required, unique or contain only specified characters.

While a schema is just an abstract representation of the data, a model on the other hand is a more practical object with methods to query, remove, update and save data from/to MongoDB. Above, we create a Character model and immediately export it.

Note Why yet another tutorial using MongoDB? Why not use MySQL, PostgreSQL, CouchDB or even Why yet another tutorial using MongoDB? Why not use MySQL, PostgreSQL, CouchDB or even RethinkDB ? That's because I don't really care enough about the database layer for the types of apps I am building. I would much rather focus on the front-end stack, because that's one of my primary interests, not databases. MongoDB may not best-suited for all use cases, but it's a decent general-purpose database and it has worked well for me in the past 3 years.

Most of these fields are pretty self-explanatory, but random and voted may need some context:

random - an array of two numbers generated by [Math.random(), 0] . It is a geospatial point as far as MongoDB is concerned. In order to grab a random character from the database we are going to use the $near operator. I found about this “trick” from Random record from MongoDB on StackOverflow.

- an array of two numbers generated by . It is a geospatial point as far as MongoDB is concerned. In order to grab a random character from the database we are going to use the operator. I found about this “trick” from Random record from MongoDB on StackOverflow. voted - a boolean for identifying which characters have already been voted. Previously, people were abusing the website by voting for the same character multiple times in a row. But now, when querying for two characters, only those characters that have not been voted will be fetched. Even if someone were to hit the API directly, a vote will not count for already voted characters.

Back in server.js, add the following lines at the beginning of the file, along with all other module dependencies:

var mongoose = require ( 'mongoose' ); var Character = require ( './models/character' );

Just to be consistent and systematic, I usually organize my module imports in the following order:

Core Node.js modules — path, querystring, http. Third-party NPM libraries — mongoose, express, request. Application files — controllers, models, config.

And finally, to connect to the database, add the following code somewhere between module dependencies and Express middlewares. This will establish a connection pool with MongoDB when we start the Express app.

mongoose . connect ( config . database ); mongoose . connection . on ( 'error' , function () { console . info ( 'Error: Could not connect to MongoDB. Did you forget to run `mongod`?' ); });

Note We will set the database hostname in config.js to avoid hard-coding the value here. We will set the database hostname in config.js to avoid hard-coding the value here.

Create another file in the top-level directory called config.js and paste the following:

module . exports = { database : process . env . MONGO_URI || 'localhost/nef' };

January 13, 2016 Update: Use localhost/nef database, because that is where we are going to import characters in the next section.

It will use an environment variable (if available) and fallback to “localhost/nef”. Using this approach allows us to use one hostname for local development and another hostname for production without updating any code, and it is especially useful when dealing with OAuth client keys and client secrets.

Now let’s import it back in server.js:

var config = require ( './config' );

Open a new tab in Terminal and run mongod . If you are on Windows, you will need to open mongod.exe in the directory where you installed MongoDB.

Step 13. Express API Routes (1 of 2)

In this section we will implement an Express route for fetching character information and storing it in database. We will be using EVE Online API for fetching Character ID, Race and Bloodline for a given character name.

Note Character gender is not a public data; it requires an API key. In my opinion, what makes New Eden Faces so great is its open nature - a user does not need to be authenticated and anyone can add any other character to the roster. That is why we have two radio buttons for gender selection on the Add Character page. It does depend on user's honesty, however. Character gender is not a public data; it requires an API key. In my opinion, what makes New Eden Faces so great is its open nature - a user does not need to be authenticated and anyone can add any other character to the roster. That is why we have two radio buttons for gender selection on the Add Character page. It does depend on user's honesty, however.

Below is a table that outlines each route’s responsibility. However, we will not be implementing all routes, because that is something you can do on your own if necessary.

Route POST GET PUT DELETE /api/characters Add a new character Get random two characters Update wins/losses for two characters Delete all characters /api/characters/:id N/A Get a character Update a character Delete a character

In server.js add the following dependencies at the top:

var async = require ( 'async' ); var request = require ( 'request' ); var xml2js = require ( 'xml2js' );

We will use async.waterfall for managing multiple asynchronous operations and request module for making HTTP requests to the EVE Online API.

Add our first route right after Express middlewares but before the “React middleware” that we created earlier in Step 8. React Routes (Server-Side).

/** * POST /api/characters * Adds new character to the database. */ app . post ( '/api/characters' , function ( req , res , next ) { var gender = req . body . gender ; var characterName = req . body . name ; var characterIdLookupUrl = 'https://api.eveonline.com/eve/CharacterID.xml.aspx?names=' + characterName ; var parser = new xml2js . Parser (); async . waterfall ([ function ( callback ) { request . get ( characterIdLookupUrl , function ( err , request , xml ) { if ( err ) return next ( err ); parser . parseString ( xml , function ( err , parsedXml ) { if ( err ) return next ( err ); try { var characterId = parsedXml . eveapi . result [ 0 ]. rowset [ 0 ]. row [ 0 ]. $ . characterID ; Character . findOne ({ characterId : characterId }, function ( err , character ) { if ( err ) return next ( err ); if ( character ) { return res . status ( 409 ). send ({ message : character . name + ' is already in the database.' }); } callback ( err , characterId ); }); } catch ( e ) { return res . status ( 400 ). send ({ message : 'XML Parse Error' }); } }); }); }, function ( characterId ) { var characterInfoUrl = 'https://api.eveonline.com/eve/CharacterInfo.xml.aspx?characterID=' + characterId ; request . get ({ url : characterInfoUrl }, function ( err , request , xml ) { if ( err ) return next ( err ); parser . parseString ( xml , function ( err , parsedXml ) { if ( err ) return res . send ( err ); try { var name = parsedXml . eveapi . result [ 0 ]. characterName [ 0 ]; var race = parsedXml . eveapi . result [ 0 ]. race [ 0 ]; var bloodline = parsedXml . eveapi . result [ 0 ]. bloodline [ 0 ]; var character = new Character ({ characterId : characterId , name : name , race : race , bloodline : bloodline , gender : gender , random : [ Math . random (), 0 ] }); character . save ( function ( err ) { if ( err ) return next ( err ); res . send ({ message : characterName + ' has been added successfully!' }); }); } catch ( e ) { res . status ( 404 ). send ({ message : characterName + ' is not a registered citizen of New Eden.' }); } }); }); } ]); });

Note I typically add block comments above my routes specifying the full path and a brief description. This allows me to quickly find the routes I am looking for using the Find...(⌘F) command as shown below. I typically add block comments above my routes specifying the full path and a brief description. This allows me to quickly find the routes I am looking for using thecommand as shown below.

Here is a step-by-step breakdown of how it works:

Get a Character ID from a Character Name. Parse XML response. Query the database to check if this character is already in the database. Pass Character ID to the next function in the async.waterfall stage. Get basic character information from a Character ID. Parse XML response. Add a new character to the database.

Go to http://localhost:3000/add then add a few characters. You could use some of the following names:

Daishan Auergni

CCP Falcon

Celeste Taylor

Note: You can find additional character names over in the EVE Online Forums.

Or better yet, download this MongoDB file dump that contains over 4000 characters and import it into your database. Please ignore “duplicate key errors” if you have already added some of the characters earlier.

In Terminal, navigate to where this file has been downloaded, then run the following command to import the characters into MongoDB:

$ mongorestore newedenfaces.bson -d nef -c characters

October 11, 2015 Update: Use explicit database and collection flags in the command above.

You will not see updated character count in the search field just yet, since we haven’t implemented an API endpoint for it. We will do that after the next section.

Next, let’s create the Home component - initial page that displays 2 characters side by side.

Step 14. Home Component

This is one of the simpler components whose only responsibility is to display 2 images and handle click events to know which one is the winning and which one is the losing character between the two.

Component

Create a new file Home.js inside components directory:

import React from 'react' ; import { Link } from 'react-router' ; import HomeStore from '../stores/HomeStore' import HomeActions from '../actions/HomeActions' ; import { first , without , findWhere } from 'underscore' ; class Home extends React . Component { constructor ( props ) { super ( props ); this . state = HomeStore . getState (); this . onChange = this . onChange . bind ( this ); } componentDidMount () { HomeStore . listen ( this . onChange ); HomeActions . getTwoCharacters (); } componentWillUnmount () { HomeStore . unlisten ( this . onChange ); } onChange ( state ) { this . setState ( state ); } handleClick ( character ) { var winner = character . characterId ; var loser = first ( without ( this . state . characters , findWhere ( this . state . characters , { characterId : winner }))). characterId ; HomeActions . vote ( winner , loser ); } render () { var characterNodes = this . state . characters . map (( character , index ) => { return ( < div key = { character . characterId } className = { index === 0 ? 'col-xs-6 col-sm-6 col-md-5 col-md-offset-1' : 'col-xs-6 col-sm-6 col-md-5' } > < div className = 'thumbnail fadeInUp animated' > < img onClick = { this . handleClick . bind ( this , character )} src = { 'http://image.eveonline.com/Character/' + character . characterId + '_512.jpg' } / > < div className = 'caption text-center' > < ul className = 'list-inline' > < li >< strong > Race : < /strong> {character.race}</ li > < li >< strong > Bloodline : < /strong> {character.bloodline}</ li > < /ul > < h4 > < Link to = { '/characters/' + character . characterId } >< strong > { character . name } < /strong></ Link > < /h4 > < /div > < /div > < /div > ); }); return ( < div className = 'container' > < h3 className = 'text-center' > Click on the portrait . Select your favorite . < /h3 > < div className = 'row' > { characterNodes } < /div > < /div > ); } } export default Home ;

July 27, 2015 Update: Fixed the error Cannot read property ‘characterId’ of undefined. I have updated how the “losing” Character ID is obtained inside handleClick() method. It uses _.findWhere to find the “winning” character object within the array, then using _.without we get a new array without the “winning” character. Since we only have 2 characters in the array, the other object must be the “losing” character. And finally, using _.first we get the first (and only) object in the array.

It is not really necessary to map over the characters array since we only have 2 characters to display, but it is one way to do it. Another way would be to create a separate markup for characters[0] and characters[1] like so:

render () { return ( < div className = 'container' > < h3 className = 'text-center' > Click on the portrait . Select your favorite . < /h3 > < div className = 'row' > < div className = 'col-xs-6 col-sm-6 col-md-5 col-md-offset-1' > < div className = 'thumbnail fadeInUp animated' > < img onClick = 