React To The Future With Isomorphic Apps

Things often come full circle in software engineering. The web in particular started with servers delivering content down to the client. Recently, with the creation of modern web frameworks such as AngularJS and Ember, we’ve seen a push to render on the client and only use a server for an API. We’re now seeing a possible return or, rather, more of a combination of both architectures happening. React has quickly risen to immense popularity in the JavaScript community. There are a number of reasons for its success. One is that Facebook created it and uses it. This means that many developers at Facebook work with it, fixing bugs, suggesting features and so on.

Things often come full circle in software engineering. The web in particular started with servers delivering content down to the client. Recently, with the creation of modern web frameworks such as AngularJS and Ember, we’ve seen a push to render on the client and only use a server for an API. We’re now seeing a possible return or, rather, more of a combination of both architectures happening.

1. What Is React?

React is a JavaScript library for building user interfaces.

According to the official website. It is a way to create reusable front-end components. Plain and simple, that is the goal of React.

1.1. What makes it different?

React has quickly risen to immense popularity in the JavaScript community. There are a number of reasons for its success. One is that Facebook created it and uses it. This means that many developers at Facebook work with it, fixing bugs, suggesting features and so on.

React To The Future With Isomorphic Apps

Another reason for its quick popularity is that it’s different. It’s unlike AngularJSBackbone.jsEmberKnockout and pretty much any of the other popular MV* JavaScript frameworks that have come out during the JavaScript revolution in the last few years. Most of these other frameworks operate on the idea of two-way binding to the DOM and updating it based on events. They also all require the DOM to be present; so, when you’re working with one of these frameworks and you want any of your markup to be rendered on the server, you have to use something like PhantomJS.

2. Virtual DOM

React is often described as the “V” in an MVC application. But it does the V quite differently than other MV* frameworks. It’s different from things like Handlebars, Underscore templates and AngularJS templates. React operates on the concept of a “virtual DOM.” It maintains this virtual DOM in memory, and any time a change is made to the DOM, React does a quick diff of the changes, batches them all into one update and hits the actual DOM all at once.

This has huge ramifications. First and foremost, performance-wise, you’re not constantly doing DOM updates, as with many of the other JavaScript frameworks. The DOM is a huge bottleneck with front-end performance. The second ramification is that React can render on the server just as easily as it can on the client.

React exposes a method called React.renderToString(). This method enables you to pass in a component, which in turn renders it and any child components it uses, and simply returns a string. You can then take that string of HTML and simply send it down to the client.

2.1. Example

These components are built with a syntax called JSX. At first, JSX looks like a weird HTML-JavaScript hybrid:

var HelloWorld = React.createClass({
  displayName: "HelloWorld",
  render() {
    return (
      <h1>Hello {this.props.message}</h1>
    );
  }
});

React.render(<HelloWorld message="world" />, document.body);

What you do with this .jsx format is pass it through (or “transpile”) webpackgruntgulp, or your “renderer” of choice and then spit out JavaScript that looks like this:

var HelloWorld = React.createClass({
  displayName: "HelloWorld",
  render: function() {
    return (
      React.createElement("h1", null, "Hello ", this.props.message)
    );
  }
});

React.render(React.createElement(HelloWorld, {message: "world"}), document.body);

That’s what our HelloWorld.jsx component transpiles to — nothing more than simple JavaScript. Some would consider this a violation of the separation of concerns by mixing JavaScript with HTML. At first, this seems like exactly what we’re doing. However, after working with React for a while, you realize that the close proximity of your component’s markup to the JavaScript enables you to develop more quickly and to maintain it longer because you’re not jumping back and forth between HTML and JavaScript files. All the code for a given component lives in one place.

React.render attaches your <HelloWorld> component to the body. Naturally, that could be any element there. This causes the component’s render method to fire, and the result is added to the DOM inside the <body> tag.

With a React component, whatever you pass in as attributes — say, <HelloWorld message=“world” /> — you have access to in the component’s this.props. So, in the <HelloWorld> component, this.props.message is world. Also, look a bit closer at the JSX part of the code:

return (
  <h1>Hello {this.props.message}</h1>
);

You’ll notice first that you have to wrap the HTML in parentheses. Secondly, this.props.message is wrapped in braces. The braces give you access to the component via this.

Each component also has access to its “state.” With React, each component manages its state with a few simple API methods, getState and setState, as well as getInitialState for when the component first loads. Whenever the state changes, the render method simply re-renders the component. For example:

var Search = React.createClass({
  getInitialState() {
    return {
      search: ""
    };
  },
  render() {
    return (
      <div className="search-component">
        <input type="text" onChange={this.changeSearch} />
        <span>You are searching for: {this.state.search}</span>
      </div>
    );
  },
  changeSearch(event) {
    var text = event.target.value;

    this.setState({
      search: text
    });
  }
});

React.render(<Search />, document.body);

In this example, the getInitialState function simply returns an object literal containing the initial state of the component.

The render function returns JSX for our elements — so, an input and a span, both wrapped in a div. Keep in mind that only one element can ever be returned in JSX as a parent. In other words, you can’t return <div></div><div></div>; you can only return one element with multiple children.

Notice the onChange={this.changeSearch}. This tells the component to fire the changeSearch function when the change event fires on the input.

The changeSearch function receives the event fired from the DOM event and can grab the current text of the input. Then, we call setState and pass in the text. This causes render to fire again, and the {this.state.search} will reflect the new change.

Many other APIs in React are available to work with, but at a high level, what we did above is as easy as it gets for creating a simple React component.

3. Isomorphic JavaScript

With React, we can build “isomorphic” apps.

i·so·mor·phic: “corresponding or similar in form and relations”

This has already become a buzzword in 2015. Basically, it just means that we get to use the same code on the client and on the server.

This approach has many benefits.

3.1. Eliminate the fouc

With AngularJS, Ember (for now) and SPA-type architecture, when a user first hits the page, all of the assets have to download. With SPA applications, this can take a second, and most users these days expect a loading time of less than two seconds. While content is loading, the page is unrendered. This is called the “flash of unstyled content” (FOUC). One benefit of an isomorphic approach to building applications is that you get the speed benefits of rendering on the server, and you can still render components after the page loads on the client.

The job of an isomorphic app is not to replace the traditional server API, but merely to help eliminate FOUC and to give users the better, faster experience that they are growing accustomed to.

3.2. Shared code

One big benefit is being able to use the same code on the client and on the server. Simply create your components, and they will work in both places. In most systems, such as RailsASP.NET MVC, you will typically have erb or cshtml views for rendering on the server. You then have to have client-side templates, such as Handlebars or Hogan.js, which often duplicate logic. With React, the same components work in both places.

3.3. Progressive enhancement

Server rendering allows you to send down the barebones HTML that a client needs to display a website. You can then enhance the experience or render more components in the client.

Delivering a nice experience to a user on a flip phone in Africa, as well as an enhanced experience to a user on a 15-inch MacBook Pro with Retina Display, hooked up to the new 4K monitor, is normally a rather tedious task.

React goes above and beyond just sharing components. When you render React components on the server and ship the HTML down to the client, React on the client side notices that the HTML already exists. It simply attaches event handlers to the existing elements, and you’re ready to go.

This means that you can ship down only the HTML needed to render the page; then, any additional things can be pulled in and rendered on the client as needed. You get the benefit of fast page loading by server rendering, and you can reuse the components.

4. Creating An Isomorphic Express App

Express is one of the most popular Node.js web servers. Getting up and running with rendering React with Express is very easy.

Adding React rendering to an Express app takes just a few steps. First, add node-jsx and react to your project with this:

npm install node-jsx --save
npm install react --save

Let’s create a basic app.jsx file in the public/javascripts/components directory, which requires our Search component from earlier:

var React = require("react"),
  Search = require("./search");

var App = React.createClass({
  render() {
    return (
      <Search />
    );
  }
});

module.exports = App;

Here, we are requiring react and our Search.jsx component. In the App render method, we can simply use the component with <Search />.

Then, add the following to one of your routers where you’re planning on rendering with React:

require("node-jsx").install({
  harmony: true,
  extension: ".jsx"
});

All this does is allow us to actually use require to grab .jsx files. Otherwise, Node.js wouldn’t know how to parse them. The harmony option allows for ECMAScript 6-style components.

Next, require in your component and pass it to React.createFactory, which will return a function that you can call to invoke the component:

var React = require("react"),
  App = React.createFactory(require("../public/javascripts/components/app")),
  express = require("express"),
  router = express.Router();

Then, in a route, simply call React.renderToString and pass it your component:

router.get("/", function(req, res) {
  var markup = React.renderToString(
    App()
  );

  res.render("index", {
    markup: markup
  });
});

Finally, in your view, simply output the markup:

<body>
  <div id="content">
    {{{markup}}}
  </div>
</body>

That’s it for the server code. Let’s look at what’s necessary on the client side.

5. Webpack

Webpack is a JavaScript bundler. It bundles all of your static assets, including JavaScript, images, CSS and more, into a single file. It also enables you to process the files through different types of loaders. You could write your JavaScript with CommonJS or AMD modules syntax.

For React .jsx files, you’ll just need to configure your webpack.config file a bit in order to compile all of your jsx components.

Getting started with Webpack is easy:

npm install webpack -g # Install webpack globally
npm install jsx-loader --save # Install the jsx loader for webpack

Next, create a webpack.config.js file.

var path = require("path");

module.exports = [{
  context: path.join(__dirname, "public", "javascripts"),
  entry: "app",
  output: {
    path: path.join(__dirname, "public", "javascripts"),
    filename: "bundle.js"
  },
  module: {
    loaders: [
      { test: /\.jsx$/, loader: "jsx-loader?harmony"}
    ]
  },
  resolve: {
    // You can now require('file') instead of require('file.coffee')
    extensions: ["", ".js", ".jsx"],
    root: [path.join(__dirname, "public", "javascripts")],
    modulesDirectories: ["node_modules"]
  }
}];

Let’s break this down:

  • context This is the root of your JavaScript files.
  • entry This is the main file that will load your other files using CommonJS’ require syntax by default.
  • output This tells Webpack to output the code in a bundle, with a path of public/javascripts/bundle.js.

The module object is where you set up “loaders.” A loader simply enables you to test for a file extension and then pass that file through a loader. Many loaders exist for things like CSS, Sass, HTML, CoffeeScript and JSX. Here, we just have the one, jsx-loader?harmony. You can append options as a “query string” to the loader’s name. Here, ?harmony enables us to use ECMAScript 6 syntax in our modules. The test tells Webpack to pass any file with .jsx at the end to jsx-loader.

In resolve we see a few other options. First, extensions tells Webpack to omit the extensions of certain file types when we require files. This allows us just to do require(“./file”), rather than require(“./file.js”). We’re also going to set a root, which is simply the root of where our files will be required from. Finally, we’ll allow Webpack to pull modules from the node_modules directory with the modulesDirectories option. This enables us to install something like Handlebars with npm install handlebars and simply require(“handlebars”), as you would in a Node.js app.

6. Client-Side Code

In public/javascripts/app.js, we’ll require in the same App component that we required in Express:

var React = require("react"),
  App = React.createFactory(require("components/app"));

if (typeof window !== "undefined") {
  window.onload = function() {
    React.render(App(), document.getElementById("content"));
  };
}

We’re going to check that we’re in the browser with the typeof window !== “undefined”. Then, we’ll attach to the onload event of the window, and we’ll call React.render and pass in our App(). The second argument we need here is a DOM element to mount to. This needs to be the same element in which we rendered the React markup on the server — in this case, the #content element.

The Search component in the example above was rendered on the server and shipped down to the client. The client-side React sees the rendered markup and attaches only the event handlers! This means we’ll get to see an initial page while the JavaScript loads.

All of the code above is available on GitHub.

7. Conclusion

Web architecture definitely goes through cycles. We started out rendering everything on the server and shipping it down to the client. Then, JavaScript came along, and we started using it for simple page interactions. At some point, JavaScript grew up and we realized it could be used to build large applications that render all on the client and that use the server to retrieve data through an API.

In 2015, we’re starting to realize that we have these powerful servers, with tons of memory and CPU, and that they do a darn good job of rendering stuff for us. This isomorphic approach to building applications might just give us the best of both worlds: using JavaScript in both places, and delivering to the user a good experience by sending down something they can see quickly and then building on that with client-side JavaScript.

React is one of the first of what are sure to be many frameworks that enable this type of behavior. Ember’s developers are already working on isomorphic-style applications as well. Seeing how this all works out is definitely going to be fun!