Socket.io for Tardy React Status Indicators

The White Rabbit, Wikimedia Commons
The White Rabbit, Wikimedia Commons

In one of my favorite movies “The Blues Brothers”, the wife of the trucker pit stop owner proudly exclaims that they play ‘both kinds of music – Country and Western’. Readers of my blog know I too tend to serve two kinds of articles. In think-pieces, I tend to pontificate on a topic that buzzes between my ears that week. In ‘hey, look at this code we wrote’, I share something we learned by doing. I am sure there is an audience for both kinds but if you are in the latter camp, this is your day.

I’m late, I’m really really late

The problem we had to solve recently was as follows: a React store contains a list of items. These items are rendered in a hierarchy of React components, with a ‘dummy’ React component rendering a card for each item. The item has a number of visual elements on it that can be easily rendered from the item’s properties, but it also contains a status indicator.

server-cards

This status indicator was killing us: when we fetch the array of items via the XHR API call, we can get them fairly quickly. However, each item represents an entity that can be working or not working (and therefore have a green or red status indicator). Computing this status takes a while, needs to be done separately for each item, and cannot hold the main XHR call.

We solved this problem with the help of the Socket.io module (at this point I need to remind you we use Node.js for our server side). You can apply the solution even if you are not using it, nor using Node at all – just use whatever Web Sockets library comes with your stack of choice.

The devil is in the pudding

OK, on to the details. What we do is as follows:

  • Whenever we want to update the status of the store, we issue an XHR.
  • The XHR fetches the items on the server via a downstream API call.
  • Before sending the response, the server endpoint initiates an array of async parallel calls to obtain status for each of the items. It sends the response to the client without waiting.
  • The client renders the cards, with the status rendered as a gray circle.
  •  When the status API calls return, the result is sent back via Web Socket channel as an event.
  • On the client, this event is converted into a Flux action, which in turn updates the Store, which updates the views.

react-status-sequence

Produce the code

OK, enough architecture, produce the code. In the interest of space conservation, I am going to assume you have rudimentary knowledge of both React and Flux. If not, Google away until React components, dispatcher, actions and stores are all concepts that make sense to you.

Our action creator is capable of dispatching two actions – fetching items and initiation service status update:

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

var ServiceConstants = require("./action-constants");

var ServiceActions = {
	fetchItems: function(data) {
		AppDispatcher.handleViewAction({
			actionType: ServiceConstants.FETCH_ITEMS,
			data: data
		});
	},
	serviceStatusUpdate: function(data) {
		AppDispatcher.handleViewAction({
			actionType: ServiceConstants.SERVICE_STATUS_UPDATE,
			data: data
		});
	}
};

module.exports = ServiceActions;

OK, on to the ItemStore. It is a fairly standard affair. When asked to fetch items, it makes an XHR request to the endpoint in the Node.js server that returns server items. When status update arrives, it merges the provided status with the items and fires a change event again. This will drive the view to re-render:

// Modules

var Constants = require("./action-constants");
var EventEmitter = require("events").EventEmitter;

// Globals

var AppDispatcher = require("./dispatcher");
var XhrUtils = require("./xhr-util");

var EVENT_CHANGE = "items";
var items;
var emitter = new EventEmitter();
var listeningForSocket = false;

// Public Methods ------------------------------------------------------------->

var ItemStore = {

	getItems: function(type) {
		return items;
	},

	emitChange: function(data) {
		emitter.emit(EVENT_CHANGE, data);
	},

	addChangeListener: function(callback) {
		emitter.on(EVENT_CHANGE, callback);
	},

	removeChangeListener: function(callback) {
		emitter.removeListener(EVENT_CHANGE, callback);
	}

};

module.exports = ItemStore;

// Private Methods ------------------------------------------------------------>

function fetchItems(url) {
	if (!url) {
		console.warn("Cannot fetch items - no URL provided");
		return;
	}

	// Fetch content
	XhrUtils.doXhr({url: url, json: true}, [200], function(err, result) {
		if (err) {
			console.warn("Error fetching assets from url: " + url);
			ItemStore.emitChange(Constants.STATE_ERROR);
			return;
		}

		items = result;
		ItemStore.emitChange(Constants.ITEMS);
	});
}

function serviceStatusUpdate(data) {
	for (var i in data) {
		var s = data[i];
		_updateStatus(s);
	}
	ItemStore.emitChange(Constants.ITEMS);
}

function _updateStatus(s) {
	for (var i in items) {
		var item = items[i];
		if (item.id===s.id) {
			item.status = s.status;
			break;
		}
	}
}

// Dispatcher ------------------------------------------------------------>

// Register dispatcher callback
AppDispatcher.register(function(payload) {
	var action = payload.action;

	// Define what to do for certain actions
	switch (action.actionType) {
		case Constants.SERVICE_STATUS_UPDATE:
			serviceStatusUpdate(action.data);
			break;
		case Constants.FETCH_ITEMS:
			fetchItems(action.data);
			break;
		default:
			return true;
	}

	return true;

});

Finally, the view. It fires the action that fetches items upon mounting, and proceeds to render the initial state with the loading indicator. Once the items are ready, it re-renders itself with the server items in place. Finally, when the status update arrives, it just re-renders and lets React diff the DOM for changed properties. Which is why we love React in the first place:

var React = require('react');
var Router = require('react-router');
var ItemStore = require('../flex/item-store');
var ActionCreator = require('../flex/action-creator');
var ActionConstants = require('../flex/action-constants');

module.exports = React.createClass({
  getInitialState: function() {
    return { loading: true, items: [] };
  },

  componentDidMount: function() {
    ItemStore.addChangeListener(this._handleAssetsChanged);
    ActionCreator.fetchItems("/api/items");
  },

  componentWillUnmount: function() {
    ItemStore.removeChangeListener(this._handleAssetsChanged);
  },

  _handleAssetsChanged: function(type) {
    if(type === ActionConstants.STATE_ERROR) {
      this.setState({
        error: "Error while loading servers"
      });
    } else {
      this.setState({
        loading: false,
        error: null,
        items: ItemStore.getItems() || []
      });
    }
  },

  render: function render() {
    var loading;
    var items;
    var error;

    if (this.state.loading) {
      loading = (
         <div className="items-loading">
            <div className="LoadingSpinner-dark"></div>
         </div>
      );
    }
    else {
      items = (
         <ul>
          {this.state.items.map(function(item) {
            var statusClass = "status-indicator";
            if (item.status==="active")
              statusClass+= " status-active";
            else if (item.status==="error")
              statusClass+= " status-error";
            else
              statusClass+= " status-loading";
            return (
               <li className="service-card" key={item.id}>
                  <div className="service-card-status">
                  <div className={statusClass}/></div>
                  <div className="service-card-name">{item.title}</div>
                  <div className="service-card-type">{item.dist}</div>
              </li>
            );
          })}
        </ul>
      );
    }
    if (this.state.error) {
      error = (
         <div className="error-box">{this.state.error}</div>
      );
    }
    return (
       <div id='list'>
          <h1>Servers</h1>
             This list shows currently available servers and their status (
             <span className="text-loading">gray</span> - loading,&nbsp; 
             <span className="text-active">green</span> - active,&nbsp; 
             <span className="text-error">red</span> - error)
          {loading}
          {items}
          {error}
      </div>
    );
  }
});

The status update portion involves Socket.io module. When the status is being computed, it is being emitted via the Web Socket. Of course, for this example we are faking the delay, but you can imagine actual status API request being involved:

var io = require('socket.io')(server);

function emitStatus(items) {
  var status = [];
  for (var i in items) {
    var item = items[i];
    var s = {};
    s.status = (Math.random()<.5)?"active":"error";
    s.id = item.id;
    setTimeout(function(status) {
      io.emit("status", [status]);
    }, (Math.floor((Math.random() * 900) + 100)), s);
  }
}

This status even is being picked up on the client, producing the status update action that is dispatched, picked up by the store and eventually causing the view to update:

  var socket = io.connect(location.origin);
  socket.on('status', function (data) {
  	ActionCreator.serviceStatusUpdate(data);
  });

Conclusion and commentary

One of the many realization we have come to using React in production is that one can get very far with views alone. A top-level view can make XHR requests, then pass the data down to child components as props. Child components can capture low-level events, and translate them into events of higher level of abstraction via callbacks passed in as props.

We realized many simple pages can be entirely done this way. What we have noticed though is that when Web Sockets and server-side events are involved, Flux architecture really comes to its own and shows its potential. Mixing server side events with events produced by users interacting with the UI is much better done using Flux.

The problem we tried to solve is rendering the initial page. If the user stays on the page for a prolonged period of time, status changes of the items can continue, but the code above can handle it without any changes. Of course, if others can create or remove server items, new events and actions are needed to model that.

As usual, the entire example is available as a Node.js app on Github, and I have deployed it in Bluemix so that you can see the app running here: https://react-status-demo.mybluemix.net. Just keep refreshing the browser to see the three-step rendering (page, then items, and finally status).

© Dejan Glozic, 2016

Advertisements

Dust.js as a React Delivery Vehicle

648px-Uniformed_Letter_Carrier_with_Child_in_Mailbag

Even people in love with Single Page Apps and pooh-poohing server side rendering know that SPAs cannot just materialize in the browser. Something has to deliver them there. Until recently, AngularJS was the most popular client side JS framework, and I have seen all kinds of ADVs (Angular Delivery Vehicles) – from Node.js (N is for Node.js in the MEAN stack), to wonderfully ironic JSP files serving Angular. Particularly exuberant developers go all the way to using static HTML to launch their apps into orbit, extolling the virtues of CDNs.

Of course, I would like to see their faces the first time they realize the need to compute something dynamically in those static files. But that is beside the point. The important takeaway is that AngularJS has a very nice property of being embeddable. You don’t need to surrender your entire page to Angular – you can sequester it to a DIV (in fact, you can have multiple Angular apps running in the same page). This is a very nice feature, but also a necessary one – again, AngularJS is a pure client side framework.

With great power…

React changes this equation because it can be rendered on the server. Similarly to AngularJS, React root component can be mounted to any node inside your page, but unlike Angular, this page can also be rendered on the server by React.

This opens the door for a whole new class of isomorphic or universal apps, and I have written about it already. What you need to decide is whether you want to render the entire page, or a subset of it. If you choose the latter, then the subsequent question becomes – how do you start the rendering process until React takes over?

My first inclination was to go full React. Using the awesome react-engine module, it is easy to configure React as the view engine in the Express framework, and render the entire page. However, we soon hit some snags:

  1. As we render our UIs using micro services that use UI composition to glue the pages together, we hit a problem of React being grumpy about having to render HTML snippet arriving from outside of its control – something much easier with a templating library such as Dust.js.
  2. JSX turned out to be fairly quirky and finicky when rendering HTML HEAD element, as well as trying to inline JavaScript in tags. In fact, the latter became so error prone that we completely gave up on it, electing to put all our script in files. I didn’t like this particular restriction, and this remains an area where I am sour on JSX and the fact it most decidedly isn’t HTML.

Of course the problems I listed above did not show up in the small example I published in the initial article. They reared their ugly head once we started getting serious. In my original example, the header was a React component. This works great if you control the entire app, but the whole deal of UI composition is to integrate with apps not necessarily written using React, and you can only do that using the least common denominator (HTML, CSS and vanilla JS).

Remind me again about UI composition

In a nutshell, UI composition is the approach where common areas on the page are served as HTML snippets from a REST API. The API service can also serve CSS and JavaScript files that accompany the snippet (if you don’t serve them from a CDN). The idea is that a dedicated microservice can be providing these common areas this way, allowing other microservices to pull the content and inline it in their pages regardless of their stack.

UI composition inverts the flow you would normally use with iframes. Instead of rendering the common areas, then placing an iframe for the content and loading the content from an external URL, we load the common area as HTML and inline it into our page. In the olden days this approach was often accomplished using ‘edge side includes’ or ESIs. This approach has many benefits, including no need for the dreaded iframes, full control over the entire page, and the ability to integrate microservices implemented using different stacks. For example, the teams in my current project use:

  1. Node.js microservices using React for isomorphic rendering
  2. Node.js microservices using Dust.js and jQuery
  3. Java microservices using AngularJS

All of these microservices render the exact same header even though they use different stacks and libraries. Powerful stuff.

OK, so React?

The problem here with React is that when we use it to render the entire page server-side, it starts to squirm in discomfort. Inlining HTML is a problem for React because, while it can be used on the server, it was designed to shine on the client. Inlining raw HTML presumably obtained as a user input into DOM is a major security exposure without sanitization, and React will tell you so in no uncertain terms. Here is the line that inlines the shared header taken straight out of our production code:

<div dangerouslySetInnerHTML={{__html: decodeURIComponent(this.props.header)}} />

This is React telling us very clearly: “I really, really, really don’t like what you are asking me to do”. And this is even before we bring in react-engine. React engine is a PayPal module that enables isomorphism when combined with Node.js/express and react-router. We can render on the server, then pack the properties as part of the HTML response, hydrate the React components on the client and continue where we left off.

Only we have a little problem: notice that in the snippet above we have inlined the header by passing it down to the React component as a prop. What this means is that this prop (‘header’) will be sent to the client alongside other properties. The only problem is that this property contains HTML snippet for the entire header, and because it can really mess up react-engine, it is also URLencoded, making it even bulkier.

This is an unhappy situation: first we inline the header HTML (which is fine), then we send that same inlined HTML, this time encoded, as a React prop to the client. This unnecessarily bloats the generated HTML.

RDV

This is what we are going to do to solve this problem: we are going to call back our trusty Dust.js sitting sad on the sidelines ever since we got smitten with React. We can press it into service to do what it does best – outline the page, prep it up to the point where React can take over.

It’s a good thing that Express framework will happily handle more than one view engine. Of course, only one of them can be registered as default, but this just means that we will need to spell out the extension of dust files (because React is the default). Big deal.


var path = require('path')
, express = require('express')
, renderer = require('react-engine')
, dust = require('dustjs-linkedin')
, helpers = require('dustjs-helpers')
, cons = require('consolidate');

...

// create the view engine with `react-engine`
var engine = renderer.server.create({
  routes: require(path.normalize(__dirname + '/public/routes.jsx')), 
  docType: ""
});

// set the engines
app.engine('.jsx', engine);
app.engine('.dust', cons.dust);

// set the view directory
app.set('views', __dirname + '/public/views');

// set jsx as the view engine
app.set('view engine', 'jsx');

// finally, set the custom view
app.set('view', renderer.expressView);

Once we have both engines set up, we can arrange our delivery vehicle like this. We will first set up a reusable layout template using Dust.js that will deliver the outline of each page:

<!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" >
   {+headContent/}
   <title>
     {title}
   </title>
   <link rel="stylesheet" href="/css/styles.css">
 </head>
 <body>
   {header|s}
   <div class="main-content">
     {+mainContent/}
   </div>
   {+javascript/}
 </body>
</html>

Notice how inlining of the header now happens in Dust.js, relieving React from doing it. We can now use this template to render our page like this:

{>"layout"/}
{<mainContent}
<div id="react-mount-node">{spa|s}</div>
{/mainContent}
{<javascript}
<script src="/bundle.js"></script>
{/javascript}

We have created a DIV in the template above where we are mounting React, and we will inline the React server-side content using the ‘spa’ variable.

Our Express controller that will handle both Dust.js and React can look something like this:

var request = require("request");

module.exports.get = function(req, res) {
  var settings = {
    title: 'SPA - Demo',
    name: 'React SPA',
    selection: 'header-spa'
  };

  var headerUrl = "http://"+req.headers.host+"/header?selection=header-spa";

  request.get(headerUrl, function (err, response, body) {
    if (err)
      console.log(err);
    else
      settings.header = body;
    res.render(req.url, { name: settings.name }, function(err, html) {
      if (err) {
        settings.spa = err.message;
        console.log(JSON.stringify(err.stack));
      }
      else
        settings.spa = html;
      res.render("spa.dust", settings); 
    });
  });
}

The code above is what I call ‘React Delivery Vehicle’. It has three steps:

  1. First we fetch the header from the URL that is providing us with the header HTML snippet. We capture it into a variable ‘header’. In production, we will heavily cache this step.
  2. Then we render the React root component as usual. This will employ react-engine and react-router to render all the views necessary for the provided request URL. However, we don’t send the rendering to the response. Instead, we capture it in the variable ‘spa’.
  3. Finally, we invoke Dust.js view engine to render the full page, passing in the ‘settings’ object. This object will contain both the header and the content rendered by React. Dust will simply inline both while rendering the page outline. The result will be sent directly to the Express response.

Commentary

The solution described above plays to the strengths of both Dust.js as a server side template engine and React as a component library. Dust.js is great at rendering HTML outline the way you would expect, with no need to worry about JSX quirks around HEAD meta tags, or JavaScript inlining. React takes over to render isomorphic components into the mount node. This means we don’t need to send any data that is not needed on the client. This fixes the HTML bloat problem I mentioned before.

As for the negatives, setting up two rendering engines on the server has a slight overhead, and switching mentally from Dust.js to JSX adds context switching tax. Luckily, you can set up reusable Dust.js templates and not really worry about them too much – most of the action will be in JSX anyway.

We like this approach and are currently switching to it across the board. If you have alternative ideas or comments, drop me a line. Meanwhile, the source code for the entire example is available on GitHub as usual, and the sample app employing this mechanism runs on Bluemix.

© Dejan Glozic, 2015

ReactJS: The Day After

A man with an excruciating headache, Wikimedia Commons
A man with an excruciating headache, Wikimedia Commons

The other day I stumbled upon a funny Onion fake news report of the local man whose one-beer plan went terribly awry. Knowing how I professed undying love to ReactJS in the previous article, and extrapolating from life that after every night on the town comes the morning of reckoning, it is time to revisit my latest infatuation.

Alas, those expecting me to declare my foolishness and heartbreak with ReactJS are hoping in vain. Instead, what you will get here is a sober (ha) account of the problems, gotchas and head scratchers we encountered running ReactJS in production. We continue to use it and plan to build our next set of micro services using it, but we have a more realistic view of it now. So let’s dive in.

  1. Code Splitting – First off, my example didn’t just use ReactJS, but also react-router and react-engine. This amazing trio together allowed us to realize the dream of isomorphic apps, where you start rendering on the server, let the browser quickly render the initial content, load JavaScript, mount React components and continue with the same code on the client.
    Nevertheless, when we got past the small example, we realized that we need to split the code we initially bundled together using browserify. At the time of this writing, code splitting is not entirely painless. React-router in its version 0.13 has examples that all presume the use of Webpack to build your JavaScript. We are using browserify and must suffer until React-router 1.0 arrives. In the mean time, we can use react-router-proxy-loader, which allows us to asynchronously load code from a bundle that does not expect Webpack.

  2. React-engine growing pain – As any new library, react-engine has some rough edges. We are happy to report that one of the issues we had with it (the inability to control how react-router is being instantiated) has already been resolved. We are hoping to be able to make react-engine omit some of the data it sends to the client because it is only ever used for server-side rendering.
  3. ReactJS id properties – React attaches ‘reactid’ data property to almost all DOM elements, using ids that are sometimes very long, resulting in situations like:
    <span data-reactid=".ejv9lnvzeo.1.2.3.0.0.$7c87c148-e1a4-4cb8-81f8-c5e74be7684b.0.1.0.0">Hello</span>
    

    If you are using gzip for the markup (as you should), these strings compress very well, but you still end up with a very messy and hard to read HTML when you view source. React team is debating back and forth on the need of these properties and they may disappear at some point in the future. I for one will not miss them.

  4. Fussy with the whitespace – While you may think when working with JSX that you are coding in HTML, you are not, and nowhere is it more apparent than when you try to add some free text in the body of HTML elements, or to mix free text and elements. JSX converts snippets of text into spans at will, resulting in HTML that bears little resemblance to the initial JSX.
    I wish there was a better way to do this. I know all the virtues of React and how JSX is most decidedly not HTML, but some things like free-form text with some embedded tags should not result in a flurry of spans (and the hated data-reactid properties).
  5. Fussy with JavaScript tags – Inserting JavaScript tags in JSX is easy if you are referencing external JS files, but if you try to inline some JavaScript right there, JSX can through you curveball after curveball until you give up and extract that code into a file. This is not a show stopper but it is annoying when you want to inline couple of lines. From the maintainability point of view, it is probably better to keep JavaScript in its own file, so I am not going to protest too loudly.

ReactJS and Web Components

As with any JS framework, making a choice is normally followed by a little nagging voice in your head concerned that you chose wrong. When it comes to religious choices (AngularJS vs ReactJS vs EmberJS etc.), there is little you can do – you just need to make a leap of faith, make sure the framework works for your particular use case and jump.

However, Web Components are something else – they promise to be ‘the native Web’ at some point, so choosing between Web Components and ReactJS is not a religious debate. Even today, with the shims it is possible to run Web Components in browsers not supporting them natively, and natively in Chrome. A growing body of reusable Web components is something you don’t want to be left out of if you are Reactified to the max.

Luckily, Andrew Rota helped out with his presentation on complementarity of ReactJS and Web Components at the recent ReactJS Conf 2015. It is worth the watch, and the skinny is that since about October 2014, custom components are a fair game in JSX. This means that you can place HTML imports in the head element, and then freely use custom components in JSX the same way you would native HTML elements.

In fact, you are not loosing out on the promise of ReactJS virtual DOM. React treats custom components the same way as native HTML components – it will compare your new render to the current DOM state and only change what needs changing (adding, removing, or changing elements and properties that are not the same). This means that you can extend the power of ReactJS to Web Components.

Of course, there are some caveats, but it turns out that things you need to care about when writing Web Components for ReactJS consumption are generally applicable. Writing small components, extremely well encapsulated, that do not leak or make assumptions about the page they are running in, or try to insert stuff outside their own boundary.

No turning back

So this turned to be a click bait of sorts, for we are not turning back from ReactJS, just learning how to use it efficiently and how to be better at it. Stay tuned for the new cool stuff we were able to do with it.

© Dejan Glozic, 2015