Building an ES6/JSX React/Flux App – Part 1 – the Views

One of the newer technologies on the JavaScript scene is Flux. It’s getting a lot of attention right now because it’s not MVC. It seems like every other framework was doing MVC or some variant thereof. Facebook (the instigators of Flux and it’s associated library – React) decided there was a better way for larger applications.

There are many articles on React and Flux – I like the introduction from Fluxxor – a library that implements the Flux architecture. However, here is the obligatory Flux architecture diagram:

08192015-1

In the Flux Architecture, there is a Dispatcher at the center of everything. The system creates Actions that are dispatched by the Dispatcher to the Stores (all the stores). Stores hold data. Not just one type of data, mind you – they could hold many types of data. The stores emit a “store changed” event when their data changes. Views listen for those changes and re-render appropriately. Those Views are inevitably React components, whose primary purpose is to minimize the DOM replacements that go on as these are slow.

In the proper Flux architecture, the Dispatcher and Stores have specific APIs and functionalities that need to be implemented. However, one can forego a lot of the code and get a much simpler (and smaller) app by simplifying and altering the API. If you use the Reflux library, for example, they forego the Dispatcher completely – insteadrelying on the actions to dispatch directly to the stores. The interesting thing about an architecture is that it is theory – the libraries implement the concrete examples.

So much for theory. I want to build an app that implements React and Flux. Before I do that, I need to know how to write React components and how to build a production workflow for an app that utilizes React. So today is all about learning the basics of React and setting up a build process.

Initializing the Project

I’m using an ASP.NET5 project for this. I could have used a Node/Express or Koa or any number of other web servers. Aside from my Web API (which comes in at the very end when I talk about authentication), I only need client-side functionality.

I’ve also added a Client directory and created an initial index.html file:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>An ES6 React/Flux Demo</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" href="bundle.css">
</head>
<body>
    <div id="root">... booting ...</div>
    <script src="bundle.js"></script>
</body>
</html>

I need a build system and I’m going to use Gulp as normal. I’ve added an NPM package.json file:

{
  "version": "1.0.0",
  "name": "react-demo",
  "private": true,
  "devDependencies": {
    "gulp": "^3.9.0",
    "gulp-less": "^3.0.3"
  }
}

I’ve also created a Gulpfile.js:

var gulp = require('gulp'),
    less = require('gulp-less');

var config = {
    src: './Client',
    dest: './wwwroot'
};

var files = {
    html: config.src + '/**/*.html',
    less: config.src + '/style/bundle.less'
};

gulp.task('build', ['style', 'html']);

gulp.task('html', function ()
{
    return gulp.src([files.html]).pipe(gulp.dest(config.dest));
});

gulp.task('style', function ()
{
    return gulp.src([files.less])
        .pipe(less())
        .pipe(gulp.dest(config.dest));
});

Once this was done, I bound the build task to the Before Build step in the Task Runner Explorer. Don’t worry about the bundle.js for now – I’ll get on to that later. If you run this project (just to see it working), you should get a browser window with a booting message displayed.

You can get my starting point from my GitHub Repository.

A Simple React Component

I’m going to write what is possibly the simplest react component ever. It just pushes out a HTML DOM. I’m going to use it to provide a title for my website. The file is located in Client/views/NavBrand.jsx – note the use of the JSX extension – that’s very important, as you will see in a moment. If you are using Visual Studio 2015 (and I suggest you do!), then use item template JSX File.

import React from 'react';

class NavBrand extends React.Component {
  render() {
    return (
      <div className="_navbrand">
        <div className="valign">
          React Demo
        </div>
      </div>
    );
  }
}

export default NavBrand;

This made me do a double-take when I first saw it. A JavaScript function is returning HTML. Actually, it’s not – there are some visual clues, such as using className instead of the normal class to specify the CSS class. There are other differences:

  • If you use label for="", the for becomes htmlFor
  • If you have a HTML element that is not terminated, you need to use /> (e.g. <img />)

There are also other differences, but I’ll get to them later. Unfortunately, Visual Studio 2015 still doesn’t like ES6 + JSX syntax (and it still has problems with ES6 in general). Atom is much better in this regard, so if you are following along on a Mac or using Node/Express, you may want to switch to Atom.

I’ve also got some style in Client/style/NavBrand.less and I’ve included that file in the bundle.less file. I do this for every single React component I write, so I’m not going to mention it again. Just be sure to pick up the less stylesheets from the repository.

This only defines the React component. I also need to use it. To do this, I’m going to create a Client/app.jsx file that I’ll use as a bootstrap:

import React from 'react';
import NavBrand from './views/NavBrand.jsx';

React.render(<NavBrand/>, document.getElementById('root'));

Building The System

I’ve got a lot going on here. My code is ECMAScript 6 and has JSX embedded in it. I also use modules that need to be dealt with. I could just write this for SystemJS (and I’ve blogged about that). Instead I’m going to use a production build system based on browserify. Browserify is a system for bundling all the libraries and JS code you use into one bundle file. All I have to do is load the libraries I want as dependencies in package.json and then run browserify. I still need to transform the ES6 and JSX code into ES5.1 for most browsers. Fortunately, there is a plugin called babelify that uses Babel for that purpose.

Since this is a production workflow, I want to bring eslint into the mix for pre-run checking. Eslint also has a plugin for React/JSX, so I’ll use that. It’s important to note here – Visual Studio 2015 doesn’t seem to like mixing ES6 and JSX. You get a lot of red-squiggly lines for perfectly valid code. As a result, you need eslint to ensure you don’t make syntax errors prior to publishing your code.

These are pretty much recipes, so I’m just copying from someone else (and I forget who, but everyone seems to use the same recipe). Here is the package.json:

{
  "version": "1.0.0",
  "name": "react-demo",
  "private": true,
  "devDependencies": {
    "babelify": "^6.1.3",
    "browserify": "^11.0.0",
    "eslint": "^0.24.1",
    "eslint-plugin-react": "^3.0.0",
    "gulp": "^3.9.0",
    "gulp-eslint": "^0.15.0",
    "gulp-less": "^3.0.3",
    "gulp-rename": "^1.2.2",
    "vinyl-source-stream": "^1.1.0"
  },
  "dependencies": {
    "react": "^0.13.3"
  }
}

I also need a task in the gulpfile.js:

var gulp = require('gulp'),
  browserify = require('browserify'),
  eslint = require('gulp-eslint'),
  less = require('gulp-less'),
  rename = require('gulp-rename'),
  source = require('vinyl-source-stream');

  // Previously discussed parts not included

gulp.task('bundle', [ 'eslint', 'copyhtml' ], function () {
  var bundler = browserify({
    extensions: ['.js', '.jsx'],
    transform: ['babelify'],
    debug: true
  });

  return bundler
    .add(files.entry)
    .bundle()
    .on('error', function(err) {
      console.error(err.toString());
    })
    .pipe(source(files.entry))
    .pipe(rename('bundle.js'))
    .pipe(gulp.dest(config.dest));
});

I also need an .eslintrc file with the following contents (just create a JSON file with the name .eslintrc):

{
  "ecmaFeatures": {
    "modules": true,
    "jsx": true
  },
  "env": {
    "browser": true,
    "es6": true
  },
  "plugins": [
    "react"
  ],
  "rules": {
    "quotes": [ 2, "single", "avoid-escape" ],
    "react/display-name": [ 1, { "acceptTranspilerName": true }],
    "react/jsx-boolean-value": 1,
    "react/jsx-no-undef": 1,
    "react/jsx-quotes": 1,
    "react/jsx-sort-prop-types": 1,
    "react/jsx-sort-props": 0,
    "react/jsx-uses-react": 1,
    "react/jsx-uses-vars": 1,
    "react/no-danger": 1,
    "react/no-did-mount-set-state": 1,
    "react/no-did-update-set-state": 1,
    "react/no-multi-comp": 1,
    "react/no-unknown-property": 1,
    "react/prop-types": 1,
    "react/react-in-jsx-scope": 1,
    "react/require-extension": 1,
    "react/self-closing-comp": 1,
    "react/sort-comp": 1,
    "react/wrap-multilines": 1
  }
}

The majority of the rules are ripped from an example in the eslint-plugin-react documentation. I’ve made some changes to suit my coding style and to support ES6.

Running the project will give you a simple piece of text. You no longer have the booting message. Instead you have a React Demo statement. That’s coming from the simple component I developed earlier.

You can get the build system plus this first component from my GitHub Repository.

React Components inside React Components

React is like many other web component technologies. You can embed components inside of components. For example, let’s say I want a NavBar.jsx component:

import React from 'react';
import NavBrand from './NavBrand.jsx';

class NavBar extends React.Component {
    render() {
        return (
            <header>
                <div className="_navbar">
                    <NavBrand/>
                </div>
                <div className="_navbar _navbar_grow">

                </div>
                <div className="_navbar">

                </div>
            </header>
        );
    }
}

export default NavBar;

I’m leaving two of the DIV elements blank, for later expansion. You can clearly see the NavBrand element embedded inside of the JSX that I am returned for the NavBar element. You can adjust the app.jsx file to bring in this component instead of NavBrand to see it in action. You can, of course, get the code from my GitHub Repository.

Reusable Components – Props

Let’s say I have a set of links I want to display. The links are defined (in app.jsx) like this:

var pages = [
      { name: 'welcome', title: 'Welcome', nav: true, auth: false, default: true },
      { name: 'flickr', title: 'Flickr', nav: true, auth: false },
      { name: 'spells', title: 'Spells', nav: true, auth: true }
];
var route = 'welcome';

I could then pass these variables down into a component like this (also in app.jsx):

React.render(<NavBar pages={pages} route={route}/>, document.getElementById('root'));

These are Properties and they appear in this.props in a React component. I need to adjust my NavBar.jsx file to account for these:

import React from 'react';
import NavBrand from './NavBrand.jsx';
import NavLinks from './NavLinks.jsx';

class NavBar extends React.Component {
    render() {
        return (
            <header>
                <div className="_navbar">
                    <NavBrand/>
                </div>
                <div className="_navbar _navbar_grow">
                    <NavLinks pages={this.props.pages} route={this.props.route}/>
                </div>
                <div className="_navbar">

                </div>
            </header>
        );
    }
}

export default NavBar;

I’ve added a new component called NavLinks that takes the properties that NavBar received. Now I need to write Client/views/NavLinks.jsx:

import React from 'react';

class NavLinks extends React.Component {
    render() {
        let visibleLinks = this.props.pages.filter(page => {
            return (page.nav === true && path.auth === false);
        });
        let linkComponents = visibleLinks.map(page => {
            let cssClass = (page.name === this.props.route) ? 'link active' : 'link';
            return (<li className={cssClass} key={page.name}>{page.title}</li>);
        });

        return (
            <div className="_navlinks">
                <ul>{linkComponents}</ul>
            </div>
        );
    }
}

export default NavLinks;

A few notes here. You can use any variable within JSX – just include it in curly braces. Note how I do iteration here – Aurelia has repeat.for, Angular has ng-repeat. React just uses JavaScript so I don’t have to learn a whole bunch of new syntax, nor do I need an extra plug-in just to iterate. I do, however, need to ensure I define a key on iterated elements. Normally, React will define a default key on all elements so that it can refer to a specific key. In the case of iterators, it can’t, so you have to do it instead.

If you build and run this project, you will get a nice list of all the pages that don’t require authentication, according to the object we pass into the root element (the NavBar in app.jsx). However, you will also note that there are errors from eslint:

08152015-1

React expects you to define the types of properties that a component can accept. The route property is a string and the pages property is a complex object. To define the property, you need to add a static object called propTypes to the class. PropTypes defines the shape of the properties that you accept. This is the way I do it:


NavLinks.propTypes = {
    pages: React.PropTypes.arrayOf(
            React.PropTypes.shape({
                auth: React.PropTypes.bool,
                nav: React.PropTypes.bool,
                name: React.PropTypes.string.isRequired,
                title: React.PropTypes.string.isRequired
            })
        ).isRequired,
    route: React.PropTypes.string.isRequired
};

Put this block right above the export statement. You can do all sorts of validation and be very prescriptive with the shape of the properties coming in. Check out the docs for the full syntax.

Since the properties for NavBar are identical to the properties for NavLinks, you can cut and paste the object to the NavBar object as well to get rid of the warnings. The code, in case you got lost, is on my GitHub Repository.

State and the Welcome Page

In the Aurelia tutorial there was a Welcome page with a form and some dynamic elements. I’m going to create two things. Firstly, I’m going to create an AppView.jsx component that represents the complete page. Here is the code:

import React from 'react';
import assign from 'lodash/object/assign';
import NavBar from '../views/NavBar';
import Welcome from '../views/Welcome';

class AppView extends React.Component {
    constructor(props) {
        super(props);

        this.state = this.props;
    }

    render() {
        let Route;
        switch (this.state.route) {
            case 'welcome': Route = Welcome; break;
            default: Route = Welcome;
        }

        return (
            <div id="pagehost">
                <NavBar pages={this.state.pages} route={this.state.route}/>
                <Route/>
            </div>
        );
    }
}

AppView.propTypes = {
    pages: React.PropTypes.arrayOf(
            React.PropTypes.shape({
                auth: React.PropTypes.bool,
                nav: React.PropTypes.bool,
                name: React.PropTypes.string.isRequired,
                title: React.PropTypes.string.isRequired
            })
        ).isRequired,
    route: React.PropTypes.string.isRequired
};

export default AppView;

Don’t forget to also alter app.jsx to render the AppView instead of the NavBar. In order to compile this, I’m using a lodash function to provide a basic object deep-copy capability, so I need to my list of dependencies in package.json. Lodash is a collection of utility functions. The assign function just copies my properties into a new thing called state.

Aside from the state thing, I’m setting this up to be a router for my page – as can be seen in the render() method. I only have one route right now, but I can add more routes as they become available.

So, why two areas to store data? Properties are defined to be immutable – you set them once and walk away. They don’t get changed within the component. State can be changed. This means I can use it to cause changes in the rendered component. Obviously, the example above (while relevant later on) is a totally useless change. I’m using state instead of props – big deal.

However, if you remember the Welcome Page from the Aurelia Tutorial, you will know we need interactivity for the page. Here is the code for Welcome.jsx:

import React from 'react';

class Welcome extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            firstname: 'John',
            lastname: 'Doe'
        };
    }

    onSubmit() {
        alert(`Hello ${this.fullname}`); // eslint-disable-line no-alert
    }

    onChange() {
        this.setState({
            firstname: React.findDOMNode(this.refs.fn).value,
            lastname: React.findDOMNode(this.refs.ln).value
        });
    }

    get fullname() {
        return `${this.state.firstname} ${this.state.lastname}`;
    }

    render() {
        let submitHandler = event => { return this.onSubmit(event); };
        let changeHandler = event => { return this.onChange(event); };

        return (
          <section id="welcome">
            <h2>Welcome</h2>

            <form role="form" onSubmit={submitHandler}>
              <div className="form-group">
                <label htmlFor="firstname">First Name</label>
                <input className="form-control" onChange={changeHandler} placeholder="First Name" type="text" value={this.state.firstname} ref="fn"/>
              </div>
              <div className="form-group">
                <label htmlFor="lastname">Last Name</label>
                <input className="form-control" onChange={changeHandler} placeholder="Last Name" type="text" value={this.state.lastname} ref="ln"/>
              </div>
              <div className="form-group">
                <label>Full Name</label>
                <p className="help-block">{this.fullname}</p>
              </div>
              <button type="submit" className="btn btn-default">Submit</button>
            </form>
          </section>
      );
    }
}

export default Welcome;

Let’s go through this slowly. The constructor sets up our initial state. I’m setting a firstname and a lastname. Next, I define an event handler that will get called when I click on Submit and an event handler when I change the text in one of the text boxes. I’ll come back to the change handler later. I’ve got a computed property next – this is used in both the submit handler to alert “Hello My Name” and within the page render.

Talking of the page render, that’s actually not too complex. Just take it slow and you will see that the JSX is practically identical to the HTML code used in the Aurelia tutorial. However, there is an interesting aspect to those input boxes. The ref attribute is not normal HTML. It’s used by React to find a specific node within this component. It’s similar to the this.$.id in Polymer, but it doesn’t use the id (which can be different). When I get to the change handler, I can use React.findDOMNode to find an element by ref.

On running this project, note the following:

  • Altering either text box will alter the computed field for Full Name
  • Pressing Submit will pop up an alert with the full name

This is a pretty basic in-component interactivity example. You can find the code on my GitHub Repository. I’ve also added a NavToolbar component to finish off the NavBar – it’s a static component for right now, just like NavBrand, so it should be familiar territory.

Wrap Up

There are obvious parallels to draw here between React and Polymer. Both are component technologies and both can render complete pages using sub-components. Both have a level of interactivity that can be built in via private event handling. Both can encapsulate the DOM elements within the component so that they can be referenced easily. Both have a full set of lifecycle functions that can be utilized to easily handle DOM behavior.

React doesn’t require any special features from the browser – it works in any modern browser. Polymer has to polyfill for missing browser features – Shadow DOM, Custom Elements, Templates, and so on. React works fine with ES6 out of the box. Polymer isn’t really ready for ES6 yet. In my tests, React was significantly faster on large DOM changes. And that is the defining thing here. React is fast. For my simple tutorial, that’s not really important. However, transition to a larger project and you really notice the slow down in the DOM when you have to re-draw complete pages.

In the end, it’s a matter of personal preference. Both are young technologies. I suspect that if you are using an MVC architecture and you want components, you will gravitate towards Polymer since they can be easily included. If you want to use a Flux architecture, then you will use React components.

In the next post, I’ll cover a “Flux” architecture and finish off the Aurelia tutorial pages with a full Flux implementation.