React By Example: Part 2

John Tucker
Frontend Weekly
Published in
5 min readMay 28, 2017

We explore build related topics; modules, media, etc.

This article is part of a series (starting with React By Example: Part 1) of articles that, through a number of progressively more complicated examples, explores the React JavaScript library. The examples are available as a GitHub repository.

Before moving onto React itself, we will explore several modern JavaScript concepts using the Create React App build tool.

Production Build

Working from our last example, reference-cra, we can run the command

yarn build

to run the production build process. The result of this command are the following files:

  • build/asset-manifest.json and build/service-worker.js: Unused in our example because we removed entry in the public/index.html file (will explore in a later article).
  • build/index.html: The public/index.html file and a <script> tag injected for the JavaScript.
  • build/static/js/main.a38c6114.js: A bundled and minified version of our JavaScript (and some additional code — discussed later); named with a unique hash.
  • build/static/js/main.a38c6114.js.map: A JavaScript source map; allows for debugging with actual source in your Browsers developer’s tools.

note: Because CRA creates an absolute reference to the JavaScript file (not sure why), we will need to use a web server to serve up the build folder. I tried to use the recommended (by CRA) serve package but it complained about my version of Node.js (mine was too new?).

I have been successfully been using the http-server package (a simple development HTTP server) for years. To install

yarn global add http-server

From the build folder, type

http-server

and open your browser to http://localhost:8080 to load the application.

As noted before, the main.a38c6114.js is significantly larger (14kB) than the original source code (285 bytes). The additional code is inserted by CRA to provide some ES2015 (the current version of JavaScript) functionality (polyfills) for our browser-ready ES5 code output, e.g., promises.

Require

A modern practice, today, is to break up JavaScript into small modular files.

  • Modules have their own variable scope (or closure); thus isolating them from each other.
  • Modules export their functionality by assigning a value to module.exports.
  • Modules import functionality from other modules using the require function.

Working from the previous example, we will split our JavaScript code into two files to illustrate these concepts.

src/index.js

var hello = require('./hello');var counterEl = document.getElementById('counter');
var incrementEl = document.getElementById('increment');
incrementEl.addEventListener('click', function() {
counterEl.innerHTML = (parseInt(counterEl.innerHTML, 10) + 1).toString();
});
hello();

src/hello.js

module.exports = function() {
window.console.log('hello world');
};

A couple of observations:

  • The immediately invoked function expression (IIFE) is no longer needed to create a scope.
  • use strict is not longer needed; it is now assumed.
  • To require files in the source directory provide the relative file path where the period refers to the current directory; you do not need to include the js extension (it is assumed).

To test, execute

yarn start

from the project folder.

Folder

If you require a folder, CRA will look for a file called index.js in it, for example we can create a folder and rename the file in our previous example and still require ./hello from within src/index.js.

src/hello/index.js

module.exports = function() {
window.console.log('hello world');
};

To test, execute

yarn start

from the project folder.

Package

Continuing from the previous example, we execute the following to install a sample node package:

yarn add sillyname

And we update:

updated src/index.js

var generateName = require('sillyname'); // ADDED FOR PACKAGE
var hello = require('./hello');
var name = generateName(); // ADDED FOR PACKAGE
var counterEl = document.getElementById('counter');
...
hello();
window.console.log(name); // ADDED FOR PACKAGE

A couple of observations:

  • To require an installed package, simply provide the package name. CRA will look for the package in the node_modules folder.
  • By convention, and sometime required by linters (code checkers), all of the require statements are listed at the top of a module.
  • Likewise, package requirements are to come before file ones.
  • Finally, the requirements are to be separated from the rest of the code by an extra line.

To test, execute

yarn start

from the project folder.

CSS

In this example, we add CSS to our previous example.

src/index.css

#counter {
color: red;
}

update src/index.js

...
var hello = require('./hello');
require('./index.css'); // ADDED FOR CSS
var name = generateName();
...

A couple of observations:

  • To bring in a CSS file simply require it; including the file extension.
  • In a production build CRA creates a single CSS file (combining all the required CSS files) as build/static/css/main.47f9206f.css (named with unique hash) and injects a link to it into the build/index.html file.
  • Unlike JavaScript modules, required CSS files share a single global namespace. While CSS modules addresses this issue, CRA does not support them and as such is outside the scope of this series.

To test, execute

yarn start

from the project folder.

Images

In this last extension in this article of the earlier example, we will incorporate images (both from CSS and JavaScript).

First we add the files src/background.jpg and src/icon.png.

updates to public/index.html

...
<button id="increment">+</button>
<div id="background"></div><!-- ADDED FOR IMAGES -->
<img id="icon" /><!-- ADDED FOR IMAGES -->
</body>
...

updated src/index.css

#counter {
color: red;
}
#background {
height: 300px;
background-image: url(background.jpg);
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}

updates to src/index.js

var generateName = require('sillyname');
var hello = require('./hello');
require('./index.css');
var icon = require('./icon.png'); // ADDED FOR IMAGES
var name = generateName();
var counterEl = document.getElementById('counter');
var incrementEl = document.getElementById('increment');
var iconEl = document.getElementById('icon'); // ADDED FOR IMAGES
incrementEl.addEventListener('click', function() {
counterEl.innerHTML = (parseInt(counterEl.innerHTML, 10) + 1).toString();
});
hello();
window.console.log(name);
iconEl.src = icon; // ADDED FOR IMAGES

A couple of observations:

  • Like normal, the background image URL is relative to the CSS file.
  • During the production build, the large background image got copied to build/static/media/background.aa19f9d5.jpg.
  • The built CSS file was updated with the new background image URL.
  • The icon was required into the JavaScript as you would another JavaScript file; the exported value is the URL of the image.
  • Unlike the background image, the icon (because it is small) gets converted into a data URL and stored inside of the JavaScript bundle file.

To test, execute

yarn start

from the project folder.

The Next Part

In the next part, React by Example: Part 3, we will learn a bit of modern JavaScript (ES2015) before moving onto React itself

--

--

John Tucker
Frontend Weekly

Broad infrastructure, development, and soft-skill background