Introduction

In the last chapter, we got introduced to the world of React. We got to know the reason behind its inception, what exactly it is, the rich and diverse ecosystem alongside it, and a couple more things.

Before, however, we could begin using React, we have to thoroughly make sure that we're aware of JavaScript pretty decently. In this chapter, we'l quickly cover the specific prequisites of JavaScript that are all vital to be able to effectively work with React in the coming chapters.

Core JavaScript along with ECMAScript 6

React is a JavaScript library and so in order to be able to work with it, one needs to be good in JavaScript, if not excellent.

React has evolved a lot over the years and so has JavaScript. React was introduced in 2013 while ECMAScript 6 was introduced in 2015. Likewise, very obviously, the earliest versions of React weren't made with ES6 in mind (it wasn't even a thing by then). But now ES6 is not just a thing; it's a norm.

And React has come to realize this and therefore, leverage the flexibility and power of the features of ES6 and beyond.

So, if you want to work with React in today's modern JavaScript era, then you must be aware of tand experienced with the ever-modern features of the language, including those covered by ES6 and later standards. These include, but aren't limited to arrow functions, array and object destructuring, default arguments, const and let, classes, and also modules.

Apart from this, you also need to understand certain theoretical aspects of JavaScript itself — ideas such as closures, this, event listeners, reflow and repaint, and so on.

Let's quickly review each of these ideas before moving over to set up our React development environment.

let and const

For a very long time, JavaScript developers were confined to using var to create variables in the language. Not just this, but there was also no way to define constants, and so the same var keyword was used to act as a polyfill. But then came in ES6, introducing two new keywords into the language — let and const

The let keyword is used to create block-scoped variables while const is used to define constants.

const is now the de facto way of defining constants in JavaScript (there's no need to use var for this). It provides the runtime safety of actual constants that can't be replicated when using var.

Talking about let, it's just a modern version of var, aimed at solving the scoping issues of var that arise in certain use cases.

Let's consider quick examples.

In the code below, we define two numeric variables x and y and a constant PI:

let x = 20;
let y = 3.02;

const PI = Math.PI;

console.log(x + y);
console.log(Math.sin(PI / 2));
23.02 1

Pretty simple, isn't it?

To learn more about let variables, refer to JavaScript Variables — The let keyword.
To learn more about const, refer to JavaScript Constants.

Throughout this course, we'll be using let when wanting to create variables, and const when wanting to create fixed, constant values.

If you're more used to var, you can keep using it as well. Just make sure that whatever you use, you remains consistent with it.

Arrow functions

As you may know, JavaScript supports many programming paradigms, one of which is the functional paradigm. Functions are first-class citizens of JavaScript, i.e. they can be used just like any other value. They can be used in expressions, stored in variables, passed into functions as arguments, and even returned from functions.

ES6 took this fact a step further and made JavaScript look more functional. It introduced a new compact syntax of defining anonymous functions — arrow functions.

There are many variations of defining arrow functions:

argumentName => returnExpression

(argumentName1, argumentName2, ...) => returnExpression

argumentName => {
   statements;
}

(argumentName1, argumentName2, ...) => {
   statements;
};

Let's quickly consider an example.

Suppose we have the following array and want to filter out those values that are greater than 50:

let nums = [1, 60, 70, -5, 32, 51];

With the normal function syntax and obviously the array filter() method, we could do this as follows:

let nums = [1, 60, 70, -5, 32, 51];
let numsAbove50 = nums.filter(function(num) {
   return num > 50
});

console.log(numsAbove50);
(3) [60, 70, 51]

But with an arrow function, we could do this same thing much more compactly, as follows:

let nums = [1, 60, 70, -5, 32, 51];
let numsAbove50 = nums.filter(num => num > 50);

console.log(numsAbove50);
(3) [60, 70, 51]

See how compact is num => num > 50 as compared to the full function defined in the previous code.

Not only are arrow functions syntactically different from normal functions, they also have different semantics. In particular, arrow functions close over the value of this lexical environment.

This is an extremely handy feature which could save us from having to create temporary _this variables just so that inner functions could use the value of this of their lexical environment.

Shown below is an example.

In the following code, we call the setTimer() method of the object obj and access the property x of the object in there, with the help of a variable _this:

let obj = {
   x: 10,
   setTimer() {
      let _this = this;
      setTimeout(function() {
         console.log(_this.x);
      }, 1000);
   }
}

obj.setTimer();

The purpose of _this is simply to save the value of this of the setTimer() method.

Here's the console log after 1s:

10

If we remove it and use this directly inside setTimeout(), this will refer to the global context, i.e. window (given that we aren't in strict mode), and ultimately this.x will evaluate to undefined:

let obj = {
   x: 10,
   setTimer() {
      setTimeout(function() {
         console.log(this.x);
      }, 1000);
   }
}

obj.setTimer();

Here's the console log after 1s:

undefined

However, because arrow functions automatically close over the binding of this in their enclosing lexical environment, we can do so using an arrow function and not have to create the _this variable.

Consider the following code:

let obj = {
   x: 10,
   setTimer() {
      setTimeout(() => {
         console.log(this.x);
      }, 1000);
   }
}

obj.setTimer();
10

Perfect!

We'll be using arrow functions a lot in the coming chapters, so it's vital to be well-versed with them.

Array and object destructuring

Prior to ES6, if we had to assign the items of an array to given variables, or if we had to assign the properties of an object to those variables, we had to do so manually.

As an example, consider the following code demonstrating this for arrays:

let point = [-5, 200];
let x = point[0];
let y = point[1];

console.log(x, y);
-5 200

And then the following code demonstrating this for objects:

let point = { x: 10, y: 20 }
let x = point.x;
let y = point.y;

console.log(x, y);
10 20

As you can see, there is absolutely no problem in doing so. But surely the whole task is a little bit lengthy.

Fortunately, with array destructuring and object destructuring introduced in ES6, we have an extremely compact way to do so.

Here are both the snippets above rewritten using array and object destructuring, respectively.

The first one:

let [x, y] = [-5, 200];

console.log(x, y);
-5 200

And now the second one:

let { x, y } = { x: 10, y: 20 }

console.log(x, y);
10 20

See how simple they both are.

React encourages us to use these destructuring patterns in our programs. The official documentation also uses this array and object destructuring syntax everywhere.

Likewise, it's important to get hands-on experience with this feature of JavaScript as well, to be able to efficiently work with React.

Classes

The first version of React created components using a createClass() method. Then later on, with the advent of ES6, this was changed to class components utilizing the class features introduced into JavaScript, including the class keyword.

However, with version 16.8, React adopted yet another way of creating components, and perhaps one which is by far the simplest of all. That is via simple functions. Components created this way are referred to as function components.

Throughout this course, we'll stick to using function components.

However, it's good to know a little bit about class components, for mainly two reasons:

  • There are still certain features in React that could only be achieved with class components at the time of this writing.
  • Class components help us easily understand the lifecycle of React components.

And in this regard, we need to know how to work with ES6 classes.

So let's quickly explore the basics of ES6 classes.

To create a class, we use the class keyword:

class className {
   // Class definition
}

All the methods of the class, static and instance, are defined inside the the pair of curly braces ({}).

The special method constructor() is used to define the constructor of the class.

For example, below we define a simple Point class with a method print() to print a readable representation of the point in the console:

class Point {
   constructor(x, y) {
      this.x = x;
      this.y = y;
   }

   print() {
      console.log(`(${this.x}, ${this.y})`);
   }
}

Let's create two points using this straightforward class and then call the print() method on both of them:

class Point { /* ... */ }

let a = new Point(10, -8);
a.print();

let b = new Point(6, 0);
b.print();
(10, -8) (6, 0)

Pretty basic.

Classes can be derived from other classes by means of using the extends keyword. In this regard, the constructor of a child class could call the constructor of the parent class using the special super() function.

An example is illustrated below:

class Parent {
   constructor() {
      this.parentProperty = 'From the parent';
   }

   parentMethod() {
      console.log(this.parentProperty);
   }
}

class Child extends Parent {
   constructor() {
      super();
      this.childProperty = 'From the child';
   }

   childMethod() {
      console.log(this.childProperty);
   }
}

We have a Parent class and then a derived class Child (inherting from Parent).

Let's go on and instantiate a Child instance and then invoke a couple of methods on it:

class Parent { /* ... */ }

class Child extends Parent { /* ... */ }

let child = new Child();
child.childMethod();
child.parentMethod();
From the child From the parent

Obviously, since the childMethod() method is defined on Child, we indeed expect the child instance to have access to it, as is evident by the first log. But, by virtue of the fact that Child extends the Parent class, the child instance also has access to the parentMethod() method defined on the Parent class, as is evident by the second log.

Note that it's important to note that extends applies the same prototypal inheritance concept to the instantiated Child instance that it would've otherwise would if we defined Child in the old constructor style.

That is, even though ES6 introduces the class keyword along with a syntax to work with OOP in JavaScript that's familiar to programmers experienced with other OOP languages (such as Java, C++, PHP), in the background, the language still uses the same old prototypal model to power OOP.

Hence, all these new ES6 class features are, more or less, just syntactic sugar over the old constructor style of defining classes in JavaScript.

Anyways, coming back to the discussion, static methods are defined using the special static keyword prepended to the method's definition.

Here's the general form of defining a static method:

class className {
   static staticMethodName(arg1, arg2, ...) {}
}

Static methods are only accessible via the name of the class in which they're defined.

Let's consider an example of a static method.

In the code below, we define a static method toPoint() on the class Point in order to obtain a Point instance from an array:

class Point {
   static toPoint(arr) {
      return new Point(arr[0], arr[1]);
   }

   constructor(x, y) {
      this.x = x;
      this.y = y;
   }

   print() {
      console.log(`(${this.x}, ${this.y})`);
   }
}

As always, let's try this new static method:

class Point { /* ... */ }

let pointArr = [-1, 100];
let point = Point.toPoint(pointArr);
point.print();
(-1, 100)

Simple, as always.

Modules

Once you start creating React applications, you'll see that it isn't common to write code all in one single file. It's customary and, in fact, desirable to break down code into multiple files to foster maintainability and scalability.

With the advent of modules in ES6, this has become the norm. Not only ES6 itself, but the advent of sophisticated bundling tools, such as Webpack and Rollup have also encouraged modularization of React applications.

Let's take a look over the basics of modules.

Modules are just normal JavaScript files, that directly refer to other files using code defined inside them.

ECMAScript modules rely on two fundamental keywords: import and export. The export keyword is used to export stuff from a module and then import is used to import that stuff in another one.

A module could have named exports or a single anonymous export, also referred to as the default export.

  • Named exports are when we directly export constants, variables, and named functions.
  • Default exports are when we export an arbitrary expression. As mentioned before, there can only be one default export within a module. A default export is denoted via the default keyword.

Let's consider a quick example.

In the greetings.js file below, we define a couple of named exports:

greetings.js
export function greet() {
   console.log('Hello!');
}

export function greetBetter() {
   console.log("Hello! Coding is awesome, isn't it?");
}

Now, let's use these exports in another main.js file in the same location as greetings.js:

main.js
import { greet, greetBetter } from './greetings.js';

greet();
greetBetter();
Hello! Hello! Coding is awesome, isn't it?

If we want to, we can even rename the imports as we perform them, using the as keyword.

In the code below, we rename greet to g and greetBetter to gb:

main.js
import { greet as g, greetBetter as gb } from './greetings.js';

g();
gb();
Hello! Hello! Coding is awesome, isn't it?

Note that when using such name aliases, the original names are no longer accessible. So, for instance, in the code above, only g and gb exist in the main.js file, NOT greet and greetBetter.

Now let's consider a default export and, thereafer, its import.

In the point.js file below we define a simple point variable representing a point on a Cartesian plane and then export it as a default export of the file:

point.js
var point = { x: 5, y: 10 };

export default point;

Now, let's head over to main.js, in the same directory, and place the following code:

import point from './point.js';

console.log(point.x, point.y);
5 10

The import statement here introduces a point identifier into the main.js module that turns out to refer to the default export of point.js.

As in the previous code snippets, we could call the default import anything we like, as shown below:

import p from './point.js';

console.log(p.x, p.y);
5 10

Not just this, but it's also possible to mix up named and default imports and exports in modules.

In the following snippets, we showcase such an example.

greetings.js
var greeting = {
   count: 0
};

export function greet() {
   console.log('Hello!');
   greeting.count++;
}

export function greetBetter() {
   console.log("Hello! Coding is awesome, isn't it?");
   greeting.count++;
}

export default greeting;
main.js
import greeting, { greet, greetBetter } from './greetings.js';

greet();
greetBetter();
console.log(greeting.count);
Hello! Hello! Coding is awesome, isn't it? 2

Easy, wasn't this?

In the coming chapters, we'll be using these import and export keywords a lot in our React projects, so once again it's important to be well-versed with them.

Closures, this and reflow/repaint

The modern features put forward by ES6 and later standards are definitely necessary for one to be able to produce simple and compact React code. However, some good old JavaScript concepts still need to be learnt for a thorough understanding of React.

Perhaps the most important concepts are as follows:

  • Function closures
  • this bindings
  • Browser reflow and repaint

Without an all-inclusive understanding of these ideas, you'll sooner or later run into issues with React, sometimes hard to debug and hard to reason.

Let's understand each of these fairly straightforward ideas quickly.

Function closures

Function closures are the backbone of function components in React. It's solely because of closures that all the code inside a function component is able to remember the state created by the component.

We cover closures in extreme depth and detail in the chapter JavaScript Functions — Closures of our JavaScript course.

The this binding

Besides this, and specifically when working with class components in React, we might need to use this in our code.

For many developers, not just React developers but also JavaScript ones, a common source of confusion is this and even the bind() function method, typically used to work around this bindings.

To learn more about this, refer to JavaScript Functions — Basics — The this keyword.
To learn more about bind() and how it relates to this, refer to JavaScript Function Methods — bind().

Reflow and repaint