React With react-engine

Previously I wrote an article about rendering react server side with seo. It was an experiment that worked but needed improvement. Lately I’ve been using react-engine to render react views client and server side. It’s a rendering engine for express that uses react-router.

I wanted to use the same react components in my previous example and modify as little as possible.
The demo will display a html select element with a list of categories. Once a category is selected a list of products, within that category, will be displayed. The select element change will fire-off an api call to get the products.

Here’s the routing I am using:

1
2
3
4
5
6
7
8
9
10
var React = require('react');
var Router = require('react-router');
var AppHandler = require('./handlers/AppHandler.jsx');
var ProductHandler = require('./handlers/ProductHandler.jsx');
var Routes = (
<Router.Route name="app" path="/" handler={AppHandler}>
<Router.Route name="products" path="/products/:category" handler={ProductHandler} />
</Router.Route>
);

I had to change AppHandler to use a new component, Layout, which serves as the main html template.

Here’s the AppHandler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var React = require('react');
var Router = require('react-router');
var Layout = require('../components/Layout.jsx');
var AppHandler = React.createClass({
render: function() {
return (
<Layout {...this.props} >
<Router.RouteHandler {...this.props} />
</Layout>
);
}
});
module.exports = AppHandler;

I pass all the properties into Layout and also into the RouteHandler. The RouteHandler is where the active route will be rendered. I use spread attributes to pass properties.

Here’s the Layout component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var React = require('react');
var CategoryList = require('./CategoryList.jsx');
var Layout = React.createClass({
render: function() {
return (
<html>
<head>
<meta charSet='utf-8' />
<title>{this.props.title}</title>
</head>
<body>
<CategoryList categories={this.props.categories} />
{this.props.children}
</body>
<script src='/bundle.js'></script>
</html>
);
};
});
module.exports = Layout;

I’ll be displaying the CategoryList component in all the views. An important part is the {this.props.children} which represents the children of the Layout component. In this case it’s the RouteHandler.

##server-side setup

  1. Require node-jsx to allow node to require jsx files.
  2. Create a template engine using react-engine.
  3. Register the template engine for jsx files.
  4. Set the template/view directory.
  5. Set jsx as the default view engine.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Make jsx file requirable by node.
require('node-jsx').install();
var express = require('express');
var app = express();
var path = require('path');
// Create the template engine with react-engine.
var engine = require('react-engine').server.create({
reactRoutes: path.normalize(path.join(__dirname, '/src/Routes.jsx'))
});
// Registers the template engine for jsx files.
app.engine('.jsx', engine);
// Set the template/view directory.
app.set('views', path.normalize(path.join(__dirname, '/src/handlers')));
// Set jsx as the view engine.
app.set('view engine', 'jsx');
// Set the custom view
app.set('view', require('react-engine/lib/expressView'));

Rendering the views on the server is easy. Pass the url and any data the view requires. Here’s an example of rendering the main route:

1
2
3
4
5
6
7
8
var express = require('express');
var router = express.Router;
router.get('/', function(req, res) {
res.render(req.url, {
title: 'Welcome!'
});
});

##client-side setup

  1. Require the react-router routes.
  2. Require all view handler files. For this example I used require-globify which allows you to require files with globbing expressions. I use browserify which is why this step is necessary.
  3. Set boot options.
  4. Boot!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var Routes = require('./Routes.jsx');
var Client = require('react-engine/lib/client');
// Include all view handler files.
require('./handlers/*.jsx', { glob: true });
// Boot options
var options = {
routes: Routes,
// Supply a function that can be called to resolove the file that
// was rendered.
viewResolver: function(viewName) {
return require('./handlers/' + viewName);
}
};
// Boot when you are ready! The DOMContentLoaded event is fired when
// the initial HTML has been completely loaded without waiting for
// stylesheets, images and subframes to finish loading.
document.addEventListener('DOMContentLoaded', function onLoad() {
Client.boot(options);
});

I created a github repo react-flux-routing-seo-revisited for this example.

Source Maps With Node.js

Using source maps when generating ES5 code from ES6 has proved to be very helpful in my development. When debugging I want errors to reference the source files not the generated files. I use babel to generate ES5 code from ES6 and source-map files. I also use source-map-support for referencing stack traces in the source file.

For this example I’ll create a simple ES6 class that extends EventEmitter and will emit a success event. I’ll purposely misspell the word emit and check if the error references the source file.

My ES6 files will be in a directory named lib. The generated ES5 and source map files will be in a directory named dist.

In my ES6 class I need to include the source-map-support library and call its install() in the construtor.
Here is the ES6 class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { EventEmitter } from 'events';
import { install } from 'source-map-support';
export class EmitStuff extends EventEmitter {
constructor() {
super();
install(); // Required for source-map-support
}
tryMe() {
this.emitt('success', 'It works!');
}
}
export default EmitStuff;

Here is the entry point that uses the EmitStuff class I created. It uses the ES5 generated file.

1
2
3
4
5
6
7
8
var EmitStuff = require('./dist/emit-stuff');
var emitStuff = new EmitStuff();
emitStuff.on('success', function(response) {
console.log(response);
});
emitStuff.tryMe();

Here is the build step that will generate ES5 code and source-map files into the dist directory from the ES6 code in the lib directory.

1
babel out-dir dist lib --source-maps

Now when I run my entry point file I get the following:

1
2
3
4
5
6
7
8
node index.js
/emitstuff/lib/emit-stuff.js:11
this.emitt('success', 'It worked!');
^
TypeError: undefined is not a function
at EmitStuff.tryMe (/emitstuff/lib/emit-stuff.js:11:8)
....

The error points to the source file!

I created a github repo, source-maps-with-node for this example.

Responsive Layouts With Stylus Jeet And Rupture

Develop for mobile first is a common theme you hear today. I use three libraries for preprocessing css to help with this process. They make it easy to get a responsive site running fairly quick.

  1. Stylus - CSS pre-processor.
  2. Jeet - Flexible ratio grid system for stylus.
  3. Rupture - Simple media queries in stylus.

Below is a sample design I’ll be working with. There is a sidebar and a content area. I’ve decided to make a breakpoint at 600 pixels. Once the browser screen size is below 600 pixels the layout will change.

alt text

This is how the screen will display once the size is below 600 pixels. I’ve stacked the sidebar and content area. I also display the links horizontally instead of vertically.

alt text

I’ll use the following stylus code to accomplish the above:

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
@import 'jeet'
#root
center(960px)
.sidebar
col(1/3)
background-color: #FFFFCC
ul
list-style: none
li
display: none
margin: 0 0 10px 0
+below(600px)
stack()
ul
center(200px)
li
display: inline
margin: 0 15px 0 0
.main
col(2/3)
background-color: #CCCCCC
+below(600px)
stack()

To summarize what I’m doing:

  • Center the document within 960 pixels.
  • Set the .sidebar element to take up 1/3 of the screen size.
  • Each .sidebar > ul > li will be displayed vertically and have a bottom margin of 15 pixels.
  • Set the .main element to take up 2/3 of the screen size.

When the screen size is below 600 pixles:

  • Stack the .sidebar and .main elements vertically.
  • Center the .sidebar > ul within 200 pixels.
  • Each .sidebar > ul > li will be displayed horizontally and have a right margin of 15 pixels.

For under 30 lines of code I accomplished a lot and set a good foundation for a responsive design. I’ll need to create a build step for the above code to be processed into css. That’s pretty easy too:

1
$ stylus -u jeet -u rupture css/*.styl --out static/bundle.css

That one liner takes the stylus file and creates a css file, bundle.css, in the directory static. I can then include the bundle.css in my html.

I created a github repo, responsive-layout for this example.

Questions? Comments? Tweet me @schempy or email schempysays@gmail.com

Async Tasks With ES6 Generators

ES6 generators seem to be a very good fit for handling asynchronous actions. I’ve been using a library, generator-runner, which uses ES6 generators for running async tasks. To use the library you create a runner that takes two parameters:

  1. A generator function. Within this function you yield to a async task. The async task is invoked with a callback to continue the generator (calling next). The callback returns the value for the yield expression. The runner then takes the value for yield expression and returns it.
  2. A callback to handle the return of the generator. The return value from the previous step is handled here.

The syntax for the above is:

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 task = require('generator-runner');
// Define the runner.
task(
function* () {
// Yield to some async task and set the value for async_result.
let async_result = yield function (next) {
// Some async task goes here
return next(null, 'result'); // Return if success OR
return next(err, undefined); // Return error
};
// Return the value from the async task.
return async_result;
},
// Callback to handle the return from generator.
function (err, ret) {
if (err) {} else {}
}
);

As an example I want to get the contents of a JSON file from a http request and display the results. The generator function will make the http request using hyperquest. The result from the http request is a readable stream. I’ll pipe the readable stream into concat-stream which fires a callback with all the data from our readable stream. Then I’ll call the callback to continue the generator (calling next) with the results from the http request.

Here’s the example:

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
42
43
44
45
var task = require('generator-runner');
var hyperquest = require('hyperquest');
var concat = require('concat-stream');
// Set URL that we want to get the contents.
let url = 'https://iojs.org/dist/index.json';
// Define our runner.
task(
function* () {
// use hyperquest to get the contents of the URL.
let stream = hyperquest(url);
// Yield to async action.
let async_result = yield function (next) {
// Pipe the readable stream, from hyperquest, into
// concat-stream and call the continuation callback
// with the results from concat-stream.
stream.pipe(concat(
function (body) {
next(null, body.toString());
}
))
// Error! Call continuation callback with error.
.on('error', function (err) {
next(err, undefined);
});
};
// Return value from async task.
return async_result;
},
// Callback to handle the return from generator.
function (err, ret) {
if (err) {
console.error(err);
} else {
console.log(ret);
}
});

I created a github repo, async-tasks-with-es6-generators for this example.
To run the example do the following:

1
2
3
$ npm install
$ npm run prepublish
$ npm start

Note:
I use babel, turns ES6 code into ES5, in the example files.

Questions? Comments? Tweet me @schempy or email schempysays@gmail.com

Streaming File Uploads With Node.js

I’ve been experimenting with Node.js to build a http server for handling POST requests. Particularly for uploading files. I would send a multipart-form/data POST request containing one or more files. The route handler would parse and stream any file to a destination. That destination could be a directory on the server or a storage service like Amazon S3.

I used routes for handling routing and busboy for parsing HTML form data. Busyboy is a perfect module for this application. It emits a file event for incoming files and a field event for each non-file field. If a file is found Busboy will emit a data event when streaming and an end event when the streaming is complete.

Here’s an example of the http server:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
var http = require('http');
var router = require('routes')();
var Busboy = require('busboy');
var port = 5000;
// Define our route for uploading files
router.addRoute('/images', function (req, res, params) {
if (req.method === 'POST') {
// Create an Busyboy instance passing the HTTP Request headers.
var busboy = new Busboy({ headers: req.headers });
// Listen for event when Busboy finds a file to stream.
busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
// We are streaming! Handle chunks
file.on('data', function (data) {
// Here we can act on the data chunks streamed.
});
// Completed streaming the file.
file.on('end', function () {
console.log('Finished with ' + fieldname);
});
});
// Listen for event when Busboy finds a non-file field.
busboy.on('field', function (fieldname, val) {
// Do something with non-file field.
});
// Listen for event when Busboy is finished parsing the form.
busboy.on('finish', function () {
res.statusCode = 200;
res.end();
});
// Pipe the HTTP Request into Busboy.
req.pipe(busboy);
}
});
var server = http.createServer(function (req, res) {
// Check if the HTTP Request URL matches on of our routes.
var match = router.match(req.url);
// We have a match!
if (match) match.fn(req, res, match.params);
});
server.listen(port, function () {
console.log('Listening on port ' + port);
});

This was a fun experiment. Handling multipart/form-data was pretty easy and the ability to stream file uploads is awesome!

I created a gist for this example stream-file-uploads-nodejs.js.

Questions? Comments? Tweet me @schempy or email schempysays@gmail.com