« Back to article list

Javascript in 2017 Primer [Node, react, es6, webpack, babel]

Table of Contents

Intro

So - you used to be the javascript whizz-kid when we still bound events directly in the DOM, the master of jquery selection driven bindings, even a dabbler into other javascript transpilers/translators/pre-processors, and now you're suddenly feeling very out of touch with the entire eco-system?

Well, this article is for you (and so was I). Hopefully by spending less than an hour reading this, you'll be up to speed, and have saved some time compared to trying to piece these things together from various sources.

By the end, you can expect to have a basic understanding of how to set up a single page app (SPA) using the following trendy tech:

React

For your view layer (it is not a full framework, its for rendering HTML in a neat way), consisting of:

  • How to load a pre-built React into your app
  • How to incorporate React (with JSX) into your app
  • How to use two React components on the same app

ES6 (ECMAScript 2015)

A cleaner/more useful language than plain old vanilla js (shout out to http://vanilla-js.com) consisting of:

  • When to use const vs let vs var for declaring variables
  • How to define an object with method properties as a class (that extends a React base class)
  • How to export and import from different files

Webpack / Babel

An ES6+ to javascript transpiler (aka, compile from one language to another, transpile!) that will show you how to:

  • Set up your webpack pre-requisites using npm (Node package manager)
  • How to build your 'dist' bundle using Babel with webpack
  • How to run with an active watcher, to immediately see your code changes in browser without requiring a re-compile each time

Getting started (setup)

So, if you're here, I didn't scare (or bore) you away, so lets begin by setting up the directory.

If you're extremely lazy (what programmer isn't?), you can just start with the git repository here, as such:

git clone https://github.com/ahungry/js-2017
npm update

Which will create all the files needed to jump right into the step-by-step portion.

If you like doing things the hard way, you can set up the files as such - first, lets make the directory and pull in the pre-built React (we don't need to load it via npm or build from source, we aren't doing anything especially fancy, and this is closer to using it over a CDN anyways):

mkdir -p ./js-2017/src && cd ./js-2017
wget https://facebook.github.io/react/downloads/react-15.0.1.zip
unzip react-15.0.1.zip
cp -ar ./react-15.0.1/build ./

Then we will set up our Node dependencies, by initializing a new package.json file (how Node tracks dependencies - similar to composer.json in Composer (PHP) or pom.xml in Maven (Java) or system.asd in Quicklisp (Common Lisp)):

npm init -y
npm install --save-dev webpack@beta babel-core babel-loader babel-preset-es2015 babel-preset-react

this may take awhile, as npm can be a little slow at times (look into the Node/npm replacement called yarn (https://yarnpkg.com/) if this bugs you).

Creating some ES6

Ok, before we get started on that, lets

Set up the HTML structure

Open a new file in the root of your project (assume you're in the directory js-2017) named index.html and put the following in it (quite a bit of this is copied from the React tutorial provided in the react-15.0.1.zip bundle):

<!DOCTYPE html>
<!-- Webpack basic tutorial -->
<!-- https://www.sitepoint.com/beginners-guide-to-webpack-2-and-module-bundling/ -->
<html>
  <head>
    <meta charset="utf-8">
    <title>Javascript for 2017</title>
  </head>
  <body>
    <h1>Basic Example with JSX and ES6 features using Webpack for modules</h1>
    <div id="clicker">Clicker goes here</div>
    <div id="container">
      <p>
        To install React, follow the instructions on
        <a href="https://github.com/facebook/react/">GitHub</a>.
      </p>
      <p>
        If you can see this, React is <strong>not</strong> working right.
        If you checked out the source from GitHub make sure to run <code>grunt</code>.
      </p>
    </div>
    <h4>Example Details</h4>
    <p>This is written with JSX with Harmony (ES6) syntax and transformed for the browser using webpack.</p>
    <p>
      Learn more about React at
      <a href="https://facebook.github.io/react" target="_blank">facebook.github.io/react</a>.
    </p>
    <p>
      Learn more about Webpack at
      <a href="https://www.sitepoint.com/beginners-guide-to-webpack-2-and-module-bundling/" target="_blank">
        sitepoint.com/beginners-guide-to-webpack-2-and-module-bundling/
      </a>.
    </p>
    <script src="./build/react.js"></script>
    <script src="./build/react-dom.js"></script>
    <script src="dist/bundle.js"></script>
  </body>
</html>

Now, you may notice dist/bundle.js doesn't exist yet - that's going to be the file built by webpack (using babel).

Now, set up your webpack configuration file

Same deal as before, for this, we're pretty much copy and pasting to get past this and get to the exciting stuff. In the file ./webpack.config.js, add the following:

// webpack.config.js
const config = {
  context: __dirname + '/src',
  entry: './app.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      include: __dirname + '/src',
      use: [
        {
          loader: 'babel-loader',
          options: {
            presets: [
              ['react'],
              ['es2015', { modules: false }]
            ]
          }
        }
      ]
    }]
  }
}

module.exports = config

Lets run across it real fast - we're defining a const (the new way to create a 'variable' in ES6, however note that it is not quite immutable as the term const would have you believe, nor is it variable - we would use a let for that).

We will talk about const/let/var a little bit later.

In this config file, you'll see it defines some basic pattern matches and include directories (all files in the ./js-2017/src directory that end in the .js extension), defines a rule that tells it to use a 'babel-loader' (the transpiler engine it will use) and some options to the loader (presets, or how it will know what transpiler rules to use - in this case, React preset is for JSX syntax, while es2015 is for the new ES6 syntax).

ES6 Fanciness - Const vs let vs var

So, a quick divergence into const vs let vs var (the TLDR, avoid var unless you have a special case requiring the scoping it provides, always prefer const over let, unless you require re-assignment, as it gives a better indication of the intent/nature of what you're setting into the variable):

Good old var

So, in good old vanilla JS, we would typically always use a var for defining a variable, and in JS, the scoping is as such:

var name = 'Jimbo'      // Think of me as 'A'

function hiName () {
  var name = 'Jon'      // Think of me as 'B'
  var name = 'Franklin' // Also think of me as 'B'
  alert('Hi ' + name)   // This is going to print, 'Hi Franklin'
}                       // When the scope ends, name goes back to its 'A' value, 'Jimbo'

hiName()                // Here the 'Hi Franklin' alert prints
alert(name)             // Here it prints 'Jimbo'

function hiNameReplaced () {
  name = 'Bobby'        // Think of me as 'C'
  alert('Hi ' + name)   // This is going to print, 'Hi Bobby'
}                       // When the scope ends, name is going to be set to its 'C' value ('Bobby')!

hiNameReplaced()        // Here the 'Hi Bobby alert prints
alert(name)             // Here it prints 'Bobby

for (var i = 0; i < 2; i++) {};
alert(i)                // Here it will print '2'

What? Well, without rescoping in the function context (by declaring with a var), any modification to the variable will change its bound value in the next scope higher (up to the global scope). This is typically referred to as lexical binding.

It will also allow the declared variable to escape a non-function scope (see, the i has escaped the for loop scope).

It's easy to see how this could lead to an error if a programmer accidentally left out a var declaration.

A new contender - let

Lets look at the previous example using the new let declaration:

let name = 'Jimbo'      // Think of me as 'A'

function hiName () {
  let name = 'Jon'      // Think of me as 'B'
  let name = 'Franklin' // Also think of me as 'B' - I'm going to error out with a redefinition error!
  alert('Hi ' + name)   // But if I didn't (if prior line was removed), this is going to print, 'Hi Jon
}                       // When the scope ends, name goes back to its 'A' value, 'Jimbo'

hiName()                // Here the 'Hi Jon' alert prints
alert(name)             // Here it prints 'Jimbo'

function hiNameReplaced () {
  name = 'Bobby'        // Think of me as 'C'
  alert('Hi ' + name)   // This is going to print, 'Hi Bobby'
}                       // When the scope ends, name is going to be set to its 'C' value ('Bobby')!

hiNameReplaced()        // Here the 'Hi Bobby alert prints
alert(name)             // Here it prints 'Bobby

for (let i = 0; i < 2; i++) {};
alert(i)                // Here it will throw an undefined variable error

So - let seems very close to var, right? Lexical scoping, similar keyword length (let and var, both 3 letters!).

For most usage, it is going almost identical - however where it differs is that something that is let is only able to be defined once per scope, this helps reduce errors in code (the value itself can be changed in the variable defined by let, it just cannot be re-let).

It also extends the function lexical scoping provided by var, and grants it to all block enclosures (for, if, try, catch, etc.).

A close relative of let, const

Oh cool, ES6 has immutable constants! Well, no, not quite - in ES6, a const functions just like let does in regards to scoping and redefinition. The main thing that separates it from let is that the variable itself cannot be redefined (it can be rescoped in blocks though!).

The reason it isn't immutable however, is that if your variable is an object (remember in JS, arrays are objects as well), the properties/values contained within can be modified without any issue.

Look at the following:

const a = 1
const a = 2 // This will throw an error about it already having been declared

const b = []
b.push(1)   // This will not error out, you'll now have b = [ 1 ]

ES6 - Back to setting up our files

Alright, so after that short divergence, lets get back to setting up our ES6 files.

We will set up our first entry point file, app.js with the following:

import Counter from './Counter'
import ExampleApplication from './Example'

var start = new Date().getTime();

setInterval(() => {
  ReactDOM.render(
      <ExampleApplication elapsed={new Date().getTime() - start} />,
    document.getElementById('container')
  );
}, 50);

ReactDOM.render(
    <Counter />,
  document.getElementById('clicker')
)

The two import files are to be defined soon, so at this point, all we're doing is introducing some JSX (a special syntax for creating DOM elements in JS - note it requires a transpiler to function, no browser natively supports it). Think of it is a very fancy macro from othre languages.

In this sample, we are having React create two new DOM elements that are defined in Counter.js and Example.js - one that re-renders every 50 milliseconds via setInterval, and one that renders once (but has a re-render routine in its class definition).

So, what is React and JSX exactly?

Both are very very popular in the JS ecosystem at the moment, so that alone may make them worth learning. React itself is a produce of Facebook, which again bolsters the product's credibility.

Given that, lets try to clarify these a little bit:

React

Without going too deep into all the performance capabilities React can provide to DOM rendering, it is essentially to the DOM what immutable functional programming is to programming languages.

Basically, it strives to ensure that the DOM is deterministic (given the same inputs / process on any given page, it will always produce the same results, no random state manipulation of DOM elements creating unpredictability).

Whether this saves development time / makes a product more maintainable is still a matter of opinion (I personally haven't seen enough React code re-use or gains over more traditional means to give a solid opinion - if you've seen some metrics on it, please comment below or email me).

JSX

Well, as mentioned, JSX is pretty close to a macro (think of a Common Lisp reader macro, or some C defmacro, but a bit fancier) - when the JSX parser sees the tags, it converts them to the appropriate document.createElement calls, and sets the attributes/nesting etc. as expected. It allows for what appears to be inline HTML/XML directly in a JS file.

Again, whether this is of net benefit or not remains to be seen (I think it makes it very difficult to judge the overall 'look' of a page by viewing snippets of code, when compared to viewing a more traditional template based DOM setup).

Just for good measure, these two snippets are equivalent (assuming you're loading your code through a JSX transpiler step):

const message = 'Hello world!'
const jsxP = <p>{message}</p>           // Notice no quotes or anything
const jsxR = React.DOM.p(null, message) // The react syntax to make a tag

and in both cases, will produce roughly the following HTML (React will actually add a few attributes that it uses for tracking the DOM elements, but that isn't really relevant here):

<p>Hello world!</p>

Alright, now we know React and JSX, back to ES6 and Example.js

So, we see we have an entry point that calls some custom ES6 classes that extend React classes in app.js.

Since our import lines were as such (in app.js):

import Counter from './Counter'
import ExampleApplication from './Example'

this implies that we will be loading the files named Counter.js and Example.js relative to our app.js file (so, js-2017/src/Counter.js and js-2017/src/Example.js). It is possible to save files with the .jsx extension (and probably recommended for heavy JSX usage), but this complicates the webpack config, and requires imports to include the extensions.

So, lets take a look at Example.js:

class ExampleApplication extends React.Component {
  render() {
    const elapsed = Math.round(this.props.elapsed  / 100);
    const seconds = elapsed / 10 + (elapsed % 10 ? '' : '.0' );
    const message =
        `React has been successfully running for ${seconds} seconds.`;

    return <p>{message}</p>;
  }
}

export default ExampleApplication

One non-ES6 thing to realize is that the render method being defined is something React needs from us in our React component we are extending. It tells React what to do (render) when the element needs to be displayed to the DOM.

A few of the ES6 things going on here are:

ES6 string interpolation

So, remember the days of having to finagle with combining JS strings, and running into issues with concatenation and numerics? (The + is used for both, and the loose typing can cause string concatenation where addition is expected etc.).

Well, you can avoid that now with the ES6 backtick operator (any JS expression can go in the ${...} segment:

const name    = 'Frank'
const message = `Hello ${name}!`

console.log(message) // Hello Frank!

ES6 class syntax

ES6 provides an easy way to define a class. Lets check a very basic example:

class Foo {
  bar() {
    console.log('It worked!')
  }
}

// Is very roughly equivalent to this vanilla JS (ignoring constructors/new keyword)
var Foo = {
  bar: function() {
    console.log('It worked!')
  }
}

Wait, didn't vanilla JS already have classes? I know I saw the new keyword used before…

function Foo () {
  this.bar = function() {
    console.log('It worked!')
  }
}

var Foo = new Foo()

So, the way of doing it like that in vanilla JS is that it is basically defining a function that when invoked with the new keyword will return an object with properties initialized to what is defined in the this context (and also execute the code defined in the function).

So, back to the syntax - in addition to creating a new object with the class syntactic sugar, we are extending the React.Component object, this ensures that we inherit (basically, in JS context, just adding new properties to an object - JS still doesn't have inheritance as thought of in other languages) the React object, and get access to some React methods we will discuss in the Counter.js example.

ES6 Export / Import

This is the new age way of handling modules (if you've tried out Node in the past, you are likely familiar with require and module.exports calls - this is quite similar, with some extra features to it).

In particular, the line:

export default ExampleApplication

sets the 'default' object exported from this file to be whatever is stored in that variable (in this case, our class object).

When we eventually call:

import ExampleApplication from './Example'

we are grabbing whatever was set as the 'default' in the export, and binding it to a variable called ExampleApplication (it's just coincidence these two names match, we could have said import Blub from './Example' and have access to our ExampleApplication class/object in the Blub variable).

ES6 Looking at Counter.js

Now lets look at Counter.js:

class Counter extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 0
    }
  }

  handleClick(event) {
    this.setState({
      count: this.state.count + 1
    })
  }

  render() {
    return <button onClick={ (e) => this.handleClick(e) }>Clicked me {this.state.count} times!</button>
  }
}

export default Counter

This introduces 2 more new ES features, the ES6 class constructor, and the arrow function.

ES6 The class constructor:

So, the constructor is simply a method defined, that when invoking a new class instance, will be run as such:

class Abc {
  constructor(n) {
    console.log(n)
  }

  foo() {
    console.log('I am the parent')
  }
}

class Xyz extends Abc {
  constructor(n) {
    super(n)
  }

  bar() {
    super.foo()
    console.log('And I am the child')
  }
}

const parent = new Abc(1) // Will console.log '1'
const child = new Xyz(2)  // Will console.log '2'
child.bar()               // Will console.log 'I am the parent', 'And I am the child'

by way of extending the class, it allows you to call the parent class constructor, by using the super method call (so, in this case, it calls React.Component.constructor).

You can also call other parent methods by using super.methodName().

ES6 Arrow Functions

Ok, this is pretty sweet, a nice concise way to define an inline function / lambda / closure / anonymous function:

const myFn = function (a, b) { return a + b }

// Is equivalent to a lambda/closure/anonymous function:
const myFnArrow = (a, b) => { return a + b }

// Which, when given a single expression in the body, is equivalent to a statement
// with an inferred return value:
const myTinyFn = (a, b) => a + b

// and when used with a single argument, can be simplified even further to this expression:
const myMicroFn = a => a + 1

Putting it all together (using webpack / babel / transpiling)

So, if you've been following along, you may have tried loading index.html in your browser, and noticed that nothing good is happening. That's because you haven't transpiled the code yet! (and as of this writing, no major browsers natively support all of ES6 or JSX).

So, hop back to your command line, and run the following:

npm run build

and notice a new file is generated if all went well (no syntax errors etc.) in dist/bundle.js - this is your transpiled file. It is your entire application stored in one JS file you can now load in almost any current browser and have function without any issue (just make sure to load in the React build files included in the repo, or pull in from a CDN).

Working on the build with a watcher

You can also use the command:

npm run start

which will cause webpack to actively watch/listen for changes in your JS files, and re-build as they are changed. This makes it very easy to program JS in the traditional fashion of saving changes, and reloading in the browser.

Comments

So, have any comments? Feel free to discuss using Disqus!

Discuss