Part 1: Building web app using react.js, express.js, node.js and mongodb

Introduction

For quite sometime now, I have been hearing some very interesting things about react.js like how fast it is and how cool it would be to pick react.js for your next web project. All that sounds very good but, unless we try it ourself, we won't be able to appreaciate it. So I decided to play with react.js for a while and see myself how accurate these claims are. Now there is just one problem, react.js documentation assumes certain level of experience with modern Javascript developement workflow, in short let's just say it's not very helpful if you are trying to use it for the first time. This series of articles is my attempt to put all the pieces together and show what it will take to build a fully functional react app.

In this series of articles, we will be creating a full stack single page JavaScript application from scratch. In Part 1 of this series we will focus completely on react.js workflow and create the front end piece of our app without any backend APIs or database. In the Part 2, we will create backend for our application using express.js and also persist our data in mongodb.

Prerequisites

In order to get the most out of this article I would suggest you to follow along. Some familiarity with node.js is required, but no prior knowledge of react.js or express.js is expected. You need to have node installed in your machine, everything else we will install as we progress.

So let's not waste any more time, open your favorite code editor and command shell, and jump right into code:

Project Setup

Like for all excellent node apps out there, story always begins with npm which is the package manage system for node.js platform. So quickly type following commands in your shell:

npm install -g gulp bower nodemon

This command will install three very useful node utilities. Gulp is a task runner which we will use to make our like easier with some automation for mundane workflow tasks, Bower is a package manager for various front end libraries like bootstrap, jquery etc. this is just a nicer way of installing these libraries in your app as opposed to downloading them manually. We will also use nodemon to automatically restart our server application whenever a code change happens.

Next let's create a clean new directory and initialize it using following commands:

mkdir school-finder
npm init
bower init

I have named my directory "school-finder" and I ll keep referring to this as root directory throughout this article, "npm" and "bower" will prompt you for several questions, just keep hitting enter and they will be happy, at this point you will be having "package.json" and "bower.json" files in your root directory. Now let's install following node modules which we will be using:

npm install --save browserify reactify vinyl-source-stream gulp react react-dom express guid

If the above list seems too long and confusing then dont worry, we are using browserify so that we can use node like CommonJs module pattern to add references to various libraries as opposed to adding script tags in html pages. reactify is there to transpile JSX to javascript. JSX is xml like JavaScript syntax which react usage, you will understand exactly what I am saying once you have written some JSX code. You can find more information about these modules in npm website, for our intent and purposes let's assume these are needed.

Now create two more directories inside root directory, "app" and "server", inside "app" directory create "actions", "components","dist" and "stores" directories. Don't worry about the directory names too much, I will explain what each directory will contain once we reach there. Next add following two file ".bowerrc" and "gulpfile.js" in the root directory of your app, edit .bowerrc file and add following code inside:

{
    "directory":"app/lib"
}

Above code essentialy configures the directory where bower will install the libraries. One last thing we need to do is add reference to bootstrap library, we need to have some basic styling in our app to make it look good, so let's run following command:

bower install --save bootstrap-css

Well that seems a lot of work, even without writting a single line of react code we have so much in our project, but trust me it's worthwhile to do all this setup. Now let's start writting some code.

First line of code

Let's add a new file under "server" directory in the root foler name "server.js", which you might have guessed by now, is going to contain our applications backend. But wait! didn't we decide to focus on only the front end code here? Yes, we are still sticking to the same plan, we just need enough backend code to get us going. So let's add following code in the server.js file

var express = require("express");
var path = require("path");

var app = express();
app.use(express.static(path.join(__dirname,"../app/dist")));
app.listen(7777,function(){
    console.log("Started listening on port", 7777);
})

So above piece of code creates an express app which listens to http requests on port 7777. It also configures express to serve static content like html, css, images etc. from school-finder/app/dist directory. Next let's add "index.html" ,"style.css"and "main.jsx" files in "app" folder and edit index.html as shown below:

<!doctype html>
<html>

<head>
    <meta charset="UTF-8">
    <title>School Finder</title>
    <link href="bootstrap.min.css" rel="stylesheet" />
    <link href="style.css" rel="stylesheet" />
</head>

<body>
    <div class="navbar navbar-default">
        <div class="container-fluid">
            <div class="navbar-header">
                <a class="navbar-brand" href="#">School Finder</a>
            </div>
        </div>
    </div>
    <div id="container" class="container"></div>
    <script src="main.js"></script>
</body>

</html>

Now launch your application using following command, and browse http://localhost:7777/index.html

nodemon  .\server\server.js

at this point if your check developer tools of your browser, you will be getting 404 errors for few resources. Did you see that coming? Let's quickly fix that, edit gulpfile.js, which we had added previously, as shown below:

var gulp = require("gulp");
var browserify = require("browserify");
var reactify = require("reactify");
var source = require("vinyl-source-stream");

gulp.task("bundle", function () {
    return browserify({
        entries: "./app/main.jsx",
        debug: true
    }).transform(reactify)
        .bundle()
        .pipe(source("main.js"))
        .pipe(gulp.dest("app/dist"))
});

gulp.task("copy", ["bundle"], function () {
    return gulp.src(["app/index.html","app/lib/bootstrap-css/css/bootstrap.min.css","app/style.css"])
        .pipe(gulp.dest("app/dist"));
});

gulp.task("default",["copy"],function(){
   console.log("Gulp completed..."); 
});

In our gulpfile we have created two tasks,

a) Task bundle, takes "main.jsx" file, which is currently empty, transpils jsx code to plain javascript using "reactify" then stream sources it using "vinyl-source-stream" further creates "main.js" file on the fly in "dist" directory with transpiled js code in it.

b) Task copy, takes index.html, bootstrap.min.css and style.css files files and copies them to "dist" folder.

now go to command shell and run "gulp" command, and then refresh the page in the browser. Check browsers developer tool and if everything went correct this far, you should not see any 404 errors.

Hello from react

I know you are getting impatient by now thinking so much work and not even a single line of react code, but remember we are trying to get a real world experience with react.js developement so keep moving. Let's add two empty files "SchoolInfo.jsx" and "SchoolsList.jsx" in "components" directory and edit them as shown below:

SchoolInfo.jsx

var React = require("react");

module.exports = React.createClass({
    render:function(){
        return(
            <div className="panel panel-default">
                <div className="panel-heading">
                    {this.props.info.name}
                </div>
                <div className="panel-body">
                    {this.props.info.tagline}
                </div>
            </div>
        )
    }
})

SchoolsList.jsx

var React = require("react");
var SchoolInfo = require("./SchoolInfo.jsx")

module.exports = React.createClass({
   render:function(){
       return(
           <div className="row">
                <div className="col-md-6">
                    //We will add addSchool functionality here
                </div>
                <div className="col-md-6">
                    {
                        this.props.schools.map(function(s,index){
                            return(
                                <SchoolInfo info={s} key={"school"+index} />
                            )         
                        })
                    }
                </div>
           </div>
       )
   } 
});

There are few important points to highlight,

a) We are able to use "require" constructs here in order to add references even though browsers do not support that construct, because we will not be adding these files references to our html files directly rather "browserify" library which we have configured as gulp task will resolve these dependencies and bundle all of them together in "main.js" file.

b) React applications are built using react coponents, you can think of components as self contained templates with functionality bundled within the template itself, although react purists will not agree with this definition, I am proposing it for the simplicity sake. We create a react component by using React.createClass method

c) The wierd looking syntax within the render function is called JSX, "reactify" which we have configured in the bundle task, trasnpiles this JSX code to JS code which browsers understand. You can write JavaScript expression within curly brackets "{}" which JSX transpiler interpolates while emitting JavaScript code.

d) Once you have created a react component, you can render that component inside another react component using familiar tag syntax, for example we have rendered SchoolInfo component inside SchoolList component using <SchoolInfo /> tag.

e) When you need to pass some data to a component, you can do so by passing objects as value to components attributes while rendering it. Whatever you pass in component's attributes will be accessible within component in the form of a property of this.props Object. For example, while rendering SchoolInfo component in SchoolList, you pass school object in "info" attribute, which you can access in SchoolInfo component by this.props.info.

Now open "main.jsx" file which is empty so far, and edit it as shown below:

var React = require("react");
var ReactDOM = require("react-dom");
var SchoolsList = require("./components/SchoolsList.jsx");

var _schools = [{name:"Lovedale",tagline:"this is a wonderful school"},
                {name:"Bishop",tagline:"Another great school"}];
                
function render(){
    ReactDOM.render(<SchoolsList schools={_schools} />, document.getElementById("container"));    
}
render();

Notice ReactDOM.render function, this indeed renders our react componet "SchoolsList" in Index.html file's div element with container id. The second parameter of ReactDOM.render function takes an html element which acts like a mount point for our entire react app. Also we are passing an array of school objects as schools attribute of SchoolList component. Now run the "gulp" command again from the command shell and refresh the page, and here you have a hello world react app.

React Flux Architecture

You must be thinking if we are going to build a whole application which is non trivial in nature with all these components, how far will I go? where will I place event handlers, all the REST API calls? How my application's architecture should look like? Well there is one answer to your all questions React Flux Architecture.

React flux is a programming patters for react applications which facebook usage internally. Flux ensures only unidirectional data flow throughout your application life cycle. Flux has following agents which keep the flux flow ticking:

  1. Actions: these are the payloads with some data and some context (type) in short objects, these are created by some helper functions as a result of an activity in the view(mostly). For example when user clicks on an add button, we will create an action which will contain infomation to be added and the context. All actions are sent to the dispacher.

  2. Dispatcher: dispatcher works like a global hub which triggers all the listeners rgistered to it whenever an action is sent to it.

  3. Stores: stores register themselves with dispatchers, when dispatcher broadcasts an actions arrival, stores update themselves if that action is relavant to those stores, and emit a change event which causes UI to get updated.

  4. Views: Views are the html rendered components.

Let's implement the FLUX in our application and it will become a little more clear:

Creating a dispatcher

In your production apps you might choose to use a robust flux library but for this article let's create our own simple dispatcher so that we can see how exactly it works. So add a new file "dispatcher.js" in "app" directory and add the following code in it which is pretty much self explanatory:

var Guid = require("guid");

var listeners = {};

function dispatch(payload) {
    for (var id in listeners) {
        listeners[id](payload);
    }
}

function register(cb) {
    var id = Guid.create();
    listeners[id] = cb;
}

module.exports = {
    register: register,
    dispatch: dispatch
}

we are going to maintain all the registered listeners in the listeners object. Whoever is interested in registering itself to dispatcher can use register method, on the other side action helpers will call dispatch mehtod of dispatcher to broadcast an action.

Creating Actions Helper

Next let's add a new file "SchoolActions.js" in "actions" folder and add following code in it:

var dispatcher = require("../dispatcher");

module.exports = {
    addSchool:function(school){
        dispatcher.dispatch({
           school:school,
           type:"school:addSchool" 
        });
    },
    deleteSchool:function(school){
        dispatcher.dispatch({
           school:school,
           type:"school:deleteSchool" 
        });
    }
}

As you can see there are two actions in our Action helper, addSchool and deleteSchool . Both actions take information in the form of school object and then add a context to it in the form of type property which tells which action will be done on which item. Whenever an action method is called (that will be from a view), it will call dispatch method of dispatcher with the payload.

Creating Store

Now is the time to implement our store, so let's add "schoolsStore.js" file in "stores" directory and change it's code as shown below:

var dispatcher = require("../dispatcher");

function SchoolStore() {
    var listeners = [];
    var schools = [{ name: "Lovedale", tagline:"A wonderful school" }, 
                    { name: "Bishop",tagline:"An awesome school" }, 
                    { name: "Daffodils", tagline:"An excellent school" }];

    function getSchools() {
        return schools;
    }

    function onChange(listener) {
        listeners.push(listener);
    }

    function addSchool(school) {
        schools.push(school)
        triggerListeners();
    }

    function deleteSchool(school) {
        var _index;
        schools.map(function (s, index) {
            if (s.name === school.name) {
                _index = index;
            }
        });
        schools.splice(_index, 1);
        triggerListeners();
    }

    function triggerListeners() {
        listeners.forEach(function (listener) {
            listener(schools);
        });
    }

    dispatcher.register(function (payload) {
        var split = payload.type.split(":");
        if (split[0] === "school") {
            switch (split[1]) {
                case "addSchool":
                    addSchool(payload.school);
                    break;
                case "deleteSchool":
                    deleteSchool(payload.school);
                    break;
            }
        }
    });

    return {
        getSchools: getSchools,
        onChange: onChange
    }
}

module.exports = SchoolStore();

In our store implementation look at the dispatcher.register method call, this is where store registers itself with the dispatcher. When dispatcher broadcasts an action, store's registered callback is called where it checks the payload's type information and decides an appropriate action whether it's addSchool or deleteSchool in our case.

Also note that after taking appropriate action in response to dispatcher's call, store calls triggerListeners, this is the final piece of the puzzle which gives UI renderer an opportuinity to update the UI by calling all the subscribers of store's onChange event.

Now let's update "main.jsx" file so that it connects to the our store rather showing dummy data.

//main.jsx
var React = require("react");
var ReactDOM = require("react-dom");
var SchoolsList = require("./components/SchoolsList.jsx");
var schoolsStore = require("./stores/schoolsStore");
var _schools = schoolsStore.getSchools();
schoolsStore.onChange(function(schools){
    _schools = schools;
    render();
});

function render(){
    ReactDOM.render(<SchoolsList schools={_schools} />, document.getElementById("container"));    
}

render();

You can run the gulp again and after refreshing the page you will see new data appears on the screen.

Addind behavior to components

Let's make our app a little more useful by adding add and delete functionality to it. We will create another react component, so let's add "AddSchool.jsx" file in "components" directory and add the following code in it:

var React = require("react");
var actions = require("../actions/SchoolActions");

module.exports = React.createClass({
    getInitialState:function(){
      return {
          name:"",
          tagline:""
      }  
    },
    addSchool:function(e){
        e.preventDefault();
        actions.addSchool(this.state);
    },
    handleInputChange:function(e){
      e.preventDefault();
      var name = e.target.name;
      var state = this.state;
      state[name] = e.target.value;
      this.setState(state);
    },
    render:function(){
        return(
            <form className="form" onSubmit={this.addSchool}>
                <div className="form-group">
                    <label className="control-label" htmlFor="name">School Name:</label>
                    <input type="text" className="form-control" id="name" name="name" value={this.state.name} onChange={this.handleInputChange} placeholder="School Name" />                    
                </div>
                <div className="form-group">
                    <label className="control-label" htmlFor="tagline">Tagline:</label>
                    <input type="text" className="form-control" id="tagline" name="tagline" value={this.state.address} onChange={this.handleInputChange} placeholder="Tagline" />                    
                </div>
                <div className="form-group">
                    <button className="btn" type="submit">Add School</button>
                </div>
            </form>
        )
    }
})

this component has some new syntax so let's quickly revisit.

  1. We have added a form onSubmit handler "addSchool". You can add as many functions as you like within createClass parameter object.

  2. Like we pass external data to a react components via attributes and we access this data by this.props object similar to that we can access internal state of our componet by this.state object. All react componets have their own internal state, and before we can use it, we need to initialize it by getInitialState function. This is a special function and the object it returns becomes the initial state of our components.

  3. Like I have mentioned earlier, react does not support two-way-binding so we need to change the state ourself whenever we feel it's relevant, for example in our case whenever user enters some values in the form controls we update the state using handleInputChange function which get's triggered from onChange event handler. Notice we have used e.preventDefault() in event handlers to avoid page refresh.

  4. When user clicks on "Add School" button, in the submit event handler we start the FLUX flow as shown in the following diagram:

react flux

  1. From Event handler we call action helper, which create an action and calls dispatcher. Dispatcher broadcasts the action and since store is subscribed to that action it updates itself and renders the UI. That is precisely the FLUX flow.

Now let's update the "SchoolsList.jsx" as shown below so that it renders the "AddSchool" component inside it:

var React = require("react");
var SchoolInfo = require("./SchoolInfo.jsx")
var AddSchool = require("./AddSchool.jsx");

module.exports = React.createClass({
   render:function(){
       return(
           <div className="row">
                <div className="col-md-6">
                    <AddSchool />
                </div>
                <div className="col-md-6">
                    {
                        this.props.schools.map(function(s,index){
                            return(
                                <SchoolInfo info={s} key={"school"+index} />
                            )         
                        })
                    }
                </div>
           </div>
       )
   } 
});

Also let's update the "SchoolInfo.jsx" to add delete functionality as shown below:

var React = require("react");
var actions = require("../actions/SchoolActions");

module.exports = React.createClass({
    deleteSchool: function(e){
        e.preventDefault();
        actions.deleteSchool(this.props.info);
    },
    render:function(){
        return(
            <div className="panel panel-default">
                <div className="panel-heading">
                    {this.props.info.name}
                    <span className="pull-right text-uppercase delete-button" onClick={this.deleteSchool}>×</span>
                </div>
                <div className="panel-body">{this.props.info.tagline}</div>
            </div>
        )
    }
})

Now run the gulp command again and refresh the page, and we have a fully functional react app running. Try adding/deleting school and it should work fine.

So now we have finished our app's front end portion, however our app has a problem, if you refresh the page, all the new schools you added or deleted, disappear. This is because we are not persisting any information yet.

In the next part of this series we will create the backend for this app and we will also revisit our front end code to implement REST API calls.

Summary

If you have tolerated the article this far then it means you want to learn react. I have attatched the sample code with this article and I encourage you to download and refer to it, in case you face any problems following along. Follow these steps to run the sample:

  1. Download and unzip
  2. Navigate to the root directory of the extracted app in your command shell
  3. Run npm install
  4. Run bower install
  5. Run gulp
  6. Run nodemon .\server\server.js
profile
Nishant Chaturvedi
Software Developer and Tech Enthusiast