React Prerequisites

Chapter 2 32 mins

Learning outcomes:

  1. A quick review of JavaScript/ES6+ features used in React
  2. The let and const keywords
  3. Arrow functions
  4. Array and object destructuring
  5. Working with classes
  6. Modules and the import/export keywords
  7. Closures, this and reflow/repaint

Introduction

In the last chapter, we got introduced to the spectacular world of React. In particular, we got to know the reason behind the inception of React, what exactly it is, the rich and diverse ecosystem surrounding it, the design ideology of React, and so on.

Now, we're good to start off learning React and getting on track towards writing our very first program in it after setting the desired environment for coding. However, before we could begin, we have to thoroughly make sure that we're aware of JavaScript pretty decently.

In this chapter, we'll quickly review some common concepts from JavaScript that are particularly useful while working with React and that'll help us easily comprehend the information in the upcoming chapters.

Core JavaScript along with ECMAScript 6

As we all know, React is a JavaScript library. So, in order to be able to work with it, one needs to be good in JavaScript.

Actually, we'll go a bit far and claim that one needs to be excellent in JavaScript in order to be able to effectively work with React. A strong foundation in JavaScript is a must.

If you want to brush up your skills in JavaScript, consider walking through the table of contents in our JavaScript course and see the areas in which you lack. Pick them up and start learning today.

React has evolved a lot over the years and so has JavaScript. Today, React leverages many of the latest features of JavaScript (from ES6, ES7, and so on).

Back when React was introduced in 2013, it didn't have many modern features in JavaScript to choose from, and was therefore pretty rudimentary in its application of concepts. But the React of today has come to realize the power of modern-day JavaScript, leveraging many of present-day ideas.

In that respect, if you want to work with React in today's modern era, then you must be aware of and experienced with the ever-modern features of JavaScript, including those covered by ES6 and later standards.

These include, but aren't limited to arrow functions, array and object destructuring, default-valued arguments (more commonly known as default parameters), 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, the this keyword, event listeners, reflow and repaint, and so on.

In the following sections, let's quickly go over some of these ideas.

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 define constants too. Not really nice.

But then came in ECMAScript 6, 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.

Talking about const, it 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 (customarily constants are named using screaming snake casing):

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.

But why? Well, this is answered in the following snippet.

Why use let and const in React?

Well, that's because let usually encourages good variable definition practices (that is, not using a variable outside its definition block, or even not using a variable before its declaration) while const makes sense whenever working with non-changing values.

Technically, we can continue using var instead of let/const but because there are certain limitations tied to var, otherwise addressed in let and const, we are rightaway encouraged to transition to let and const when working with React.

Plus, no one can deny the fact the let and const both give a more modern touch to code, and that aligns well with a modern tool such as React.

Actually, as you'll soon see, const will have a much larger usage percentage, owing to the nature of values we'll be frequently dealing with in React.

Arrow functions

As you may know, JavaScript supports many programming paradigms; one of them is the functional paradigm.

Functions are first-class citizens in JavaScript, i.e. they can be used just like any other value in the language. They can be used in expressions, stored in variables, passed into functions as arguments, and even returned from functions.

ES6 took this a step further and made JavaScript look even more functional. It introduced a new compact syntax of defining anonymous functions (a common trait of functional languages) — 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 — a simple functional programming task:

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);
[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);
[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 from their outer lexical environment.

Regular functions don't close over the value of this from their outer environment; instead, they have their own this binding.

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 need to refer to a property x of the object obj inside a method of the same object, initTimer(), using this:

In the following code, we call the initTimer() 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,

   initTimer() {
      let _this = this;
      setTimeout(function() {
         console.log(_this.x);
      }, 1000);
   }
}

obj.initTimer();

Clearly, since the reference to this (for obj) has to be made inside the callback passed to setTimeout(), we can't just use the keyword as is — that would refer to the this of this callback, NOT to the this of initTimer().

That is, we can NOT do the following:

let obj = {
   x: 10,

   initTimer() {
      setTimeout(function() {
console.log(this.x); // Can't directly refer to `this`! }, 1000); } } obj.initTimer();

The this keyword inside setTimeout()'s callback will resolve to window (as we're in non-strict mode), and then accessing x on it will resolve to undefined.

Here's the console output of this code after 1s:

undefined

Clearly, not what we want!

As shown before, we first need to save the this of initTimer() inside a temporary variable, which we call _this, and then use this variable in setTimeout()'s callback.

let obj = {
   x: 10,

   initTimer() {
let _this = this; // this is obj setTimeout(function() {
console.log(_this.x); // So, _this is obj }, 1000); } } obj.initTimer();

Here's the console log after 1s:

10

Good.

But keep in mind that this behavior only applies when we're working with regular functions, not with arrow functions. Arrow functions automatically close over the binding of this in their enclosing lexical environment.

This means that we can rewrite the code above using an arrow function without having to create the _this variable, and just sticking to directly using this.

Consider the following code:

let obj = {
   x: 10,

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

Since setTimeout()'s callback here is an arrow function, its this has the same value as this from the outer environment, which in this case is the initTimer() function where this is just obj.

Perfect!

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

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 given variables, we had to do so manually.

As an example, consider the following code demonstrating this for arrays. point represents a point on a 2D plane, x represents its x coordinate, while y represents its y coordinate:

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.

Still the same prototypal inheritance model!

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 done if we defined Child in the old constructor style (using Child.prototype and Parent.prototype).

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 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 very quick look over the basics of modules.

To learn about JavaScript modules in detail, starting with their history from the early days, refer to the JavaScript Modules — Introduction.

Modules are basically just JavaScript files that refer to other JavaScript files using code.

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 module.

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 directory 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 a main.js file in the same directory and place the following code:

main.js
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.

Since the default import is just an expression, we could call the default import anything we like without requiring any special treatment (like the as keyword), 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 constructs a lot in our React projects, so once again it's important to be well-versed with them.

However, unlike above, the module specifiers (the part after the from keyword) won't be exact file names but rather shortened names without the .js extensions, all thanks to bundler tools that do the file resolution themselves.

We'll keep the anticipation intact and let all this be explored in the next React Setup chapter.

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

As we'll eventually see, function closures are perhaps the most used of JavaScript's concepts in React. Every single React app uses closures — literally every single.

Modern-day React relies heavily on functions, even nested functions. Event listeners (functions themselves) are defined inside functions and work with data obtained from the enclosing functions.

Closures are what enable such kinds of data retrievals in functions from enclosing functions in JavaScript. We cover closures in extreme depth and detail in JavaScript Functions: Closures.

The this binding

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

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. And to learn more about bind() and how it relates to this, refer to JavaScript Function Methods: bind().

Reflow and repaint