Learning React.js by example Part 2

May 31st, 2016

AprilTitle (2)

In my previous article, I outlined the basics of how to learn React and went through a practical example. In order to keep things simple, we skipped over a few important things that I promised to revisit in this article.

In this article we will cover:

  1. Add Node Package Manager (NPM), Webpack and Split up our React.js components
  2. Incorporate ES6 syntax
  3. Refactor to use a JavaScript date library (moment.js)
  4. Investigate a Flux implementation (for the Model and the Controller)

AprilGuy2
Add Node Package Manager (NPM)

Node Package Manager, also called NPM, is a JavaScript package manager. For those of you familiar with Ruby, you can think of it as a combination of bundler and ruby-gems. For the rest of you, you can think of it as a way to package up JavaScript libraries and allow them to be easily reused. Even though it is called “Node” package manager, you don’t need to use Node.js to use it. In our example, we are not using Node and it will work fine to manage our React.js components and dependencies.

Webpack is a module bundler that takes all of your code and npm packages and compiles or transpiles it into JavaScript such that the browser can understand it. Here is a visual representation of what is happening.

Figure A

node-webpack

To get started with NPM and Webpack, first install NPM, and then create a new folder called Unfuddle_activity_2 and change into that folder.

There is a great article here that takes you through all the steps to get your React project setup with Webpack and Node. Instead of repeating this article, I am going to show you what my Unfuddle application looks like after setting it up this way.

First, let’s look at our folder structure and files:

Figure B

Screen Shot 2016-05-27 at 2.41.05 PM

Now, instead of one giant Unfuddleactivity.js file as we had in part one of the article, we are able to have a much more intuitive layout. This layout has a “app” folder where we place all our components, and each component is in its own file. Next, we have a “public” folder which is where our deployed code goes. This includes the index.html and the final “bundle.js” file which is generated by compiling the source plus the dependent node packages as shown in Figure A.

To make all this work, we need a few configuration files. Let’s take a look at each of these:
File: webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var webpack = require('webpack');
var path = require('path');
 
var BUILD_DIR = path.resolve(__dirname, 'src/client/public');
var APP_DIR = path.resolve(__dirname, 'src/client/app');
 
var config = {
  entry: APP_DIR + '/unfuddle-es6.jsx',
  module : {
    loaders : [
      {
        test : /\.jsx?/,
        include : APP_DIR,
        loader : 'babel'
      }
    ]
  },
  output: {
    path: BUILD_DIR,
    filename: 'bundle.js'
  }
};
 
module.exports = config;

The webpack.config.js file configures how Webpack builds the bundle.js file. This is a fairly simple config. If you look at it, it just takes all the jsx files in /client/app and builds them into a bundle.js file. The unfuddle-es6.jsx file is the entry point of the app.

File: package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
  "name": "unfuddle_activity_2",
  "version": "1.0.0",
  "description": "My NPMized Unfuddle Project",
  "main": "index.html",
  "dependencies": {
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.6.0",
    "babel-preset-react": "^6.5.0",
    "react": "^15.0.0",
    "react-dom": "^15.0.0",
    "webpack": "^1.12.14"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

The package.json file configures how NPM is setup. For the most part, this just tells NPM which packages are needed for this app. This allows you to not have to check all your NPM packages into production and when other people pull your application they can just do a “NPM install” to get all the packages.

File: .babelrc

1
2
3
{
  "presets" : ["es2015", "react"]
}

The .babelrc file controls the jsx transpiling to js using the bable compiler. In this case, we just tell it to use the ES6 (es2015) and jsx (React) plugins.

Now that we have our configuration and our file structure all setup, let’s take a look at the React components and see what changes we needed to make:
File: ItemBox.jsx

1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
import ItemList from './ItemList.jsx';
import ItemForm from './ItemForm.jsx';
import FilterForm from './FilterForm.jsx';
var $ = require("jquery");
 
// The container of all components
var ItemBox = React.createClass({
  ..... // I have omitted the implementation so we can focus on the component changes.
});
export default ItemBox;

Looking at our ItemBox.jsx file, there are 2 key changes.

First, we need to import or require our dependencies. On line 1-4, we import our other components we need to use and on line 5, we require JQuery which is an external NPM package we need. Lastly, at the end of the file, we need to export our component as “ItemBox,” so other components can use it.

This concludes the section on setting up NPM for use with React.js. You can view the full source of my converted project here on github.

Incorporate ES6 syntax

ES6, also known as ES2015, is a major upgrade to the JavaScript language. Each of us has key things we like about ES6, but there are tons of changes. To view a good listing of all the upgrades, this is a good site. For me, I have 2 favorites that I want to talk about. First is the ability to do traditional class definitions like many other languages vs prototype class definitions. For example, to define a Shape class in ES5 with a constructor and move method, it would look like this:

1
2
3
4
5
6
7
8
var Shape = function (id, x, y) {
    this.id = id;
    this.move(x, y);
};
Shape.prototype.move = function (x, y) {
    this.x = x;
    this.y = y;
};

This is not how I am used to looking at class and method declarations. Lucky for me in ES6, this now looks like:

1
2
3
4
5
6
7
class Shape {
 constructor (id, x, y) {
    this.id = id this.move(x, y) 
 }
 move (x, y) {
    this.x = x this.y = y } 
 }

This looks much more like most OO languages I have used for the past 20 years, C++, Java, Smalltalk & Ruby.

My second favorite thing so far is the Arrow Functions. Not because I like the look of them (they still look confusing to me), but because they propagate “this” how you would think it works. Lets take a look at an example.

1
2
3
4
var self = this; 
this.nums.forEach(function (v) {
 if (v % 5 === 0) self.fives.push(v); 
});

In ES5, when we wanted to have “this” available to a function, we had to do a little trick where outside the function we set a variable “self” (can be called anything) to the value of “this” and then reference self within the function. This always seemed kludgy to me. Now with ES6 Arrow functions, we can do:

1
2
3
this.nums.forEach((v) => 
{ if (v % 5 === 0) this.fives.push(v) 
})

With the Arrow functions, “this” behaves how we think it should.

Now, let’s take a look at React components before and after ES6. First, let’s look at Item.jsx and you’ll notice, this is a simple component:

With ES5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import React from 'react';
 
var Item = React.createClass({
  removeRecord: function(e){
      this.props.handleItemRemove(this);
  },
  render: function() {
        var extraData = "";
        var rec_id = this.props.record_id;
        // Some items have various extra data, this is to grab this data by item type.
        // Could probably be a case statement.
        if(this.props.record_type === "Comment")
        {
          extraData = this.props.record.comment.body;
          rec_id = this.props.record.comment.parent_id;
 
        }
        if(this.props.record_type === "Changeset")
        {
            extraData = this.props.record.changeset.message;
            rec_id = this.props.record.changeset.parent_id;
 
        }
        if(this.props.record_type === "Ticket")
        {
            extraData = this.props.record.ticket.summary;
            rec_id = this.props.record.ticket.id;
        }
 
        return (
                {this.props.event}:
                {this.props.action_date}
                {this.props.description}
                {this.props.person_name}({this.props.person_id})
                {this.props.record_type}
                {rec_id}
                {extraData}
           ); 
      } 
     });
  export default Item;

With ES6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Component to show an activity item returned from unfuddle.
import React from 'react';
 
class Item extends React.Component
{
 
  removeRecord(e) {
    this.props.handleItemRemove(this);
  }
  render() {
    var extraData = "";
    var rec_id = this.props.record_id;
    // Some items have various extra data, this is to grab this data by item type.
    // Could probably be a case statement.
    if (this.props.record_type === "Comment") {
      extraData = this.props.record.comment.body;
      rec_id = this.props.record.comment.parent_id;
    }
    if (this.props.record_type === "Changeset") {
      extraData = this.props.record.changeset.message;
      rec_id = this.props.record.changeset.parent_id;
    }
    if (this.props.record_type === "Ticket") {
      extraData = this.props.record.ticket.summary;
      rec_id = this.props.record.ticket.id;
    }
    return (
           {this.props.event}:
           {this.props.action_date}
           {this.props.description}
           {this.props.person_name}({this.props.person_id})
           {this.props.record_type}
           {rec_id}
           {extraData}
     );
   }
  } 
 
export default Item;

For me, this is a much cleaner traditional OO look!

Refactor to use a JavaScript date library (moment.js)

Now that we have NPM, this is easy. We just install moment using – “NPM install moment –save”. This will update our package.json file and install moment in our local node_modules folder.

In part one of our article, we had some ugly js code that looked like this:

1
2
3
4
5
6
7
8
9
10
    var today = new Date();
    var dd = today.getDate();
    var mm = today.getMonth()+1; //January is 0!
    var yyyy = today.getFullYear();
    var endDate = yyyy + "/" + mm + "/" + dd
    today.setDate(today.getDate()-14);
    var dd = today.getDate();
    var mm = today.getMonth()+1; //January is 0!
    var yyyy = today.getFullYear();
    var startDate = yyyy + "/" + mm + "/" + dd

Now using moment helpers, we can simplify this to:

1
2
    var startDate = moment().subtract(14, "days").format("YYYY/MM/DD");
    var endDate = moment().format("YYYY/MM/DD");

Investigate a Flux implementation (for the Model & Controller)

In our last section, we investigated how we would use a Flux implementation to better organize how we get data from an API and to our React.js components. For this section, we are going to focus on Redux which is one Flux implementation. There are others out there, but we will focus on only Redux for this article. Flux is a data flow architecture designed by Facebook and Redux is an implementation of this architecture. To be clear, Flux is not the MC in MVC, but it is an alternate design pattern for getting data to and from your views. See here for a comparison of MVC and Flux.

Getting your head around Redux takes some time. In this article, we are just going to introduce the concepts and the rough idea of how to move from our existing JQuery inline calls to a Flux architecture with Redux.

The first concept to understand is with Redux, you put your application state (different than component state) in a big state tree. Anytime you need to get or update data, you update this application state tree. See the Redux site for more details.

Getting data into the application state tree is done using Action Creator‘s which create actions, and get data and return a payload. See here for more details on Action Creators. Middleware can be used to manage these actions.

Getting data out of the application state tree is done using Reducers. Reducers in effect watch nodes of the application state tree and update component state accordingly.

Here is what our Unfuddle Application would look like in Redux.

Figure C

redux

To explain more, in Figure C, we would add a new Action Creator which would be a function to get the data from Unfuddle. This action creator would return an action with a type of “FETCH_ITEMS” and a payload of the items and the people we got back from the Unfuddle API. Remember when we did the Ajax calls we needed to use promises to get the data back. In the case of Redux, we want to return the promise from the Action Creator and let Redux middleware redux-promise resolve the promises and send back the data to the reducers.

The ItemReducer would then update the application state for fetchedItems and people when they come through.

Now that we have an updated application state, we need to map our application state of fetchedItems to our ItemBox’s properties called data (fetchedItems) and people. To do this mapping, Redux provides a mapStateToProps function that allows us to define this relationship.

Now you have an introduction to Redux and understand the basics of Application State, Action Creator’s, Actions, Middleware and Reducers. Getting a full understanding of these concepts is beyond the scope of an article. I highly recommend the Udemy course called “Modern React with Redux”, where you can get a deep dive of how to use Redux with React.js.

Summary

In this article, we learned how to add NPM to our example application and split out our React components into individual files. We also learned some ES6 basics and how to incorporate them into React.js. We simplified our date manipulation and formatting using the moment.js library. This also allowed us to see how to include other packages using NPM. Lastly, we got a high level introduction to Redux and how our application would look using Redux. I hope you found these articles helpful. Recently, I gave a presentation on these 2 articles at Devignition 2016 and you can watch the video here.