0% found this document useful (0 votes)
41 views

Understanding React Simplest Practical (001 036)

This document is a summary of a book about understanding React. It begins with an introduction to essential JavaScript concepts needed to understand React like variables, functions, arrays, objects, classes, modules and promises. It then covers essential React concepts such as components, props, state, events and hooks. The book is very practical, describing how to build a simple CRUD application and blog using React principles. It also covers authentication, authorization and security topics like cross-site scripting when building applications.

Uploaded by

ismail başaran
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
41 views

Understanding React Simplest Practical (001 036)

This document is a summary of a book about understanding React. It begins with an introduction to essential JavaScript concepts needed to understand React like variables, functions, arrays, objects, classes, modules and promises. It then covers essential React concepts such as components, props, state, events and hooks. The book is very practical, describing how to build a simple CRUD application and blog using React principles. It also covers authentication, authorization and security topics like cross-site scripting when building applications.

Uploaded by

ismail başaran
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 36

Understanding

React
The Simplest Practical Guide
to Start Coding in React

Includes:
React Hooks and React Router
+ Full Stack Authentication
& Authorization Flow

Enrique Pablo Molinari


2

While the author have used good faith efforts to ensure that the information
and instructions contained in this work are accurate, the author disclaim all
responsibility for errors or omissions, including without limitation responsibility
for damages resulting from the use of or reliance on this work. Use of the
information and instructions contained in this work is at your own risk. If any
code samples or other technology this work contains or describes is subject
to open source licenses or the intellectual property rights of others, it is your
responsibility to ensure that your use thereof complies with such licenses
and/or rights.
Contents

About the Author 5

What is this book about? 6

Development Environment 7

I Introduction 8
1 Essential JavaScript Concepts 9
1.1 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.3 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.4 Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.4.1 A Prototype-Base Language . . . . . . . . . . . . . . . 19
1.5 Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.6 The Multiple Meanings of this . . . . . . . . . . . . . . . . . . 24
1.7 Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1.8 Single Thread Language . . . . . . . . . . . . . . . . . . . . . 30
1.9 The Promise Object and the async/await Keywords . . . . . . 32

II Understanding React 37
2 Essential React Concepts 38
2.1 React Principles . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.2 Creating a React Project . . . . . . . . . . . . . . . . . . . . . 39
2.3 React Components . . . . . . . . . . . . . . . . . . . . . . . . 40
2.4 Rendering Components . . . . . . . . . . . . . . . . . . . . . . 41
2.4.1 Styling . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.5 Props . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
2.6 State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

3
CONTENTS 4

2.7 Dealing with Events . . . . . . . . . . . . . . . . . . . . . . . 55


2.8 Hooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.8.1 useState . . . . . . . . . . . . . . . . . . . . . . . . . . 60
2.8.2 useEffect . . . . . . . . . . . . . . . . . . . . . . . . . . 63

III Practical React 72


3 A Simple CRUD Application 73
3.1 Material UI . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
3.2 Identifying Components . . . . . . . . . . . . . . . . . . . . . 74
3.3 Children Prop . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
3.4 Conditional Rendering . . . . . . . . . . . . . . . . . . . . . . 84
3.5 Components Communication . . . . . . . . . . . . . . . . . . . 90
3.6 Custom Environment Variables . . . . . . . . . . . . . . . . . 91
3.7 Data Grids . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
3.8 Dialog Box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
3.9 Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
3.10 Re Write with Class-Based Components . . . . . . . . . . . . 115

4 Creating a Blog 116


4.1 Identifying Components . . . . . . . . . . . . . . . . . . . . . 116
4.1.1 Components and APIs . . . . . . . . . . . . . . . . . . 118
4.2 React Router . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
4.2.1 Defining Routes . . . . . . . . . . . . . . . . . . . . . . 122
4.2.2 Navigation . . . . . . . . . . . . . . . . . . . . . . . . . 124
4.2.3 Programmatically Navigation . . . . . . . . . . . . . . 128

5 Authentication and Authorization 139


5.1 Task List Application . . . . . . . . . . . . . . . . . . . . . . . 139
5.2 XSS and CSRF . . . . . . . . . . . . . . . . . . . . . . . . . . 144
5.2.1 Same-Origin Policy . . . . . . . . . . . . . . . . . . . . 147
5.2.2 Cross-Origin Resource Sharing . . . . . . . . . . . . . . 148
5.2.3 Token Storage . . . . . . . . . . . . . . . . . . . . . . . 150
5.2.4 Best Practices . . . . . . . . . . . . . . . . . . . . . . . 150
5.3 Login and Private Routes . . . . . . . . . . . . . . . . . . . . 151
5.4 Logout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
About the Author

My name is Enrique Pablo Molinari. I have been working in the software


industry for the last 22 years, working in different software projects from
different companies as developer, technical lead and architect. I’m a passionate
developer and also a passionate educator. In addition to my work on the
software industry, I’m teaching Object Oriented Design and Advance Database
Systems at Universidad Nacional de Río Negro.

Understanding React is my second book. I have also written Coding


an Architecture Style, a book about hands-on software architecture. You
can find more about my thoughts on software development at my blog:
Copy/Paste is for Word. I would be very happy if you want to ping me by
email at [email protected] to send thoughts, comments or questions
about this book, the other or my blog.

5
What is this book about?

Every successful framework or library provides something unique which gives


developers a new tool for writing better software. In the case of React, that
tool is called component. You might be thinking that you have been reading
about components as the solution to your spaghetti software nightmare for
the last 15 years without any success. You are not wrong. However, React is
an exception. It provides the constructions and tools to build highly cohesive
components to assemble your next application. In this book, we will study
React core concepts, to end up being very practical describing how to split an
application into components and to fully implement it. But before that, it is
necessary to study some Javascript concepts. Understanding these concepts
will make you a better React developer. If you are already a Javascript
developer, then you can just ignore the initial chapter. But if your experience
is mainly on server side programming languages like Java, C#, PHP, etc, the
initial chapter will give you the necessary basis.

6
Development Environment

There are many development environments out there, and you can choose
the one you are more comfortable with. In any case if you don’t have
a preference, I recommend Visual Studio Code (VS Code). And to be
more productive, especially if you are new to React, I suggest installing
the extension VS Code ES7 React/Redux/React-Native/JS snippets which
provides JavaScript and React snippets. I would also suggest installing
Prettier, which is a JavaScript/React code formatter.
To install an extension, in Visual Studio Code, go to the File menu, then
Preferences and then Extensions. You will see a search box that will allow
you to find the extensions that you want to install.
Finally, I really recommend configuring VS Code to format your source
files on save. You can do that by going to the File menu, then Preferences
and then Settings. On the search box type Editor: Format On Save. This
will format your code right after you save it.

7
Part I

Introduction

8
Chapter 1

Essential JavaScript Concepts

You can use React just by learning from React docs and you will also be able
to build applications, without digging into JavaScript. However, if you want
to master React and that means, understand why and how certain things
work, you must learn some specific concepts from JavaScript.
In this chapter we will explain those JavaScript concepts and syntactical
constructions needed to make a solid learning path to React. If you want
to dig in more details on some of the topics explained here or others about
JavaScript I recommend to visit the Mozilla[1] web site. Indeed, this section
is based on learning paths and ideas taken from there. Having said that, let’s
begin.
From the Developer Mozilla JavaScript Documentation [1] JavaScript is
defined as:
“JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled
programming language with first-class functions. While it is most well-known
as the scripting language for Web pages, many non-browser environments also
use it, such as Node.js, Apache CouchDB and Adobe Acrobat. JavaScript
is a prototype-based, multi-paradigm, single-threaded, dynamic language,
supporting object-oriented, imperative, and declarative styles.”

If you are a Java, C# or C++ developer that definition might sound


a bit intimidating. The thing is that you do have to learn some concepts.
Especially those that are not available in compiled languages (if your experience
comes from there). To start with these concepts we will first explain basic
language constructions and with that in place we will explain what it means
for a language to have first-class functions and to be prototype-based,
multi-paradigm, single-threaded and dynamic.

The JavaScript language is governed by a standard under the responsibility

9
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 10

of ECMA[3]. ECMAScript is the name of the language specification. The


standardisation allows vendors to write interpreters and developers to be able
to run their programs on any vendor interpreter. So, it is a great thing. In
2015 there was a major release known as ES6, ECMAScript 6 or ECMAScript
2015. Most of the syntactical constructions that we will study in this chapter
were implemented in this release.

Let’s then begin learning. Any piece of code written in this chapter will
run using the node interpreter. Install the latest LTS version of Node.js.
Once installed, you can verify that it is working by open a console and type:

$ node -v

That will display the version installed. With this in place, you can execute
a JavaScript file in the following way:

$ node yourjsfile.js

Using Visual Studio Code, you can go to the menu "Terminal" and then
"New Terminal". It will open a small terminal window where you can run
your scripts.

Printing text on the screen must be the very first thing you learn every
time you start playing with a new programming language. This book is not
the exception. You can print text on the screen using the Console object
like the following example:

1 console.log("Coding in React!");

Let’s then open VS Code to try this out. Open a console from your
operating system, create a new folder called ’chapter1’, and then type: code
chapter1. That will open VS Code ready to be used inside of the ’chapter1’
folder. Create a new file called console.js, copy and paste the previous
snippet in that file, save it and execute it typing:

$ node console.js

The object Console was created mainly for debugging purposes, it should
not be used in production. It gives you access to the Browser’s debug console.
It is also not part of the standard, but most modern browsers and nodejs
supports it.
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 11

1.1 Variables
Let’s move to variables. You can declare a variable using the let keyword:

1 let myFirstVariable;

Declaring a variable without initialising it will assign the undefined special


value to it and that is what you will see if you print it. Try it!. Let’s give it
an initial string value:

1 let myFirstVariable = "Hello value!";

Now if you print it you will see the string. You can also declare a variable
using the const keyword:

1 const myFirstConst = "Hello constant value!";

As you might have guessed, declaring a variable with const will not allow
you to change the value of the variable once it has been initialised. If you do
it the interpreter will throw an error. Try it!.

JavaScript is a dynamic language, among many other things that we


will discuss later, that means that the type of a variable can be changed
at runtime. Opposed to static (or compiled) languages where the type of a
variable is defined at compile-time and cannot be changed during execution.

1 //my type is string


2 let changeMyType = "Hello String!";
3 //now it is number
4 changeMyType = 100;

1.2 Functions
Let’s move now to functions. In the following code snippet we are declaring
a function and after that we are calling it:

1 function saySomething(string) {
2 console.log(string);
3 }
4

5 saySomething("Hello Function!");
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 12

If you just need to have a function that gets called as soon as it is declared,
you can use the syntax below. This is called IIFE (Immediately-invoked
Function Expression).

1 (function saySomething(string) {
2 console.log(string);
3 })("Hello Function!");

In JavaScript functions always return something. If you don’t explicitly


return something from the function using the return keyword it will return
undefined.

1 let x = saySomething("Hello Function!");


2 //x is undefined

In addition, functions are first-class objects. The most well known


first class object of programming languages are variables. Variables are
denominated first-class objects because it can be assigned, it can be passed
as argument to a function or method, it can be returned from a function,
etc. So, having functions as first-class objects means that all those things
that you can do with variables are possible with functions too. See at the
example below where on line 6 we are assigning the function to the variable
say. And then on line 10 we are using it to invoke the function.

1 function returnSomething(string) {
2 return "This is it: " + string;
3 }
4

5 //assigning a function to a variable


6 let say = returnSomething;
7

8 //calling the function


9 returnSomething("Hello js!");
10 say("Hello again!");

Both say and returnSomething points to the same place which is the
first statement in the body of the function. In the next example, on line
12 we are invoking a function and passing the returnSomething function as
argument. Note how then is invoked on line 8 and its return value returned.

1 function returnSomething(string) {
2 return "This is it: " + string;
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 13

3 }
4

5 //receives a function as parameter


6 //invokes it and return the value
7 function saySomethingMore(fn) {
8 return fn("Hey !");
9 }
10

11 //passing a function as argument


12 saySomethingMore(returnSomething); //"This is it: Hey !"
Functions can also be assigned to variables just in its declaration as the
next example illustrate:
1 //assigning the function
2 const returnSomething = function (string) {
3 return "This is it: " + string;
4 };
5

6 returnSomething("Hey !"); //"This is it: Hey !"


JavaScript provides another and a bit less verbose way to declare functions
called arrow functions. Let’s see some examples:
1 //arrow function with no parameters
2 const arrowf1 = () => {
3 return "arrowf1 was invoked!";
4 };
5

6 //arrow function with one parameter


7 //parenthesis is not necessary here
8 const arrowf2 = param => {
9 return "this is the argument: " + param;
10 };
11

12 //arrow functions with one statement


13 //in the body won't need return
14 const arrowf3 = (a, b) => a + b;

1.3 Arrays
Arrays are another very important construction that we will use massively.
This is how you can declare an array:
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 14

1 //an empty array


2 let empty = [];
3

4 //an array
5 let family = ["Jóse", "Nicolas", "Lucia", "Enrique"];

The elements of an array can be accessed by its index, where the index
of the first element is 0.

1 //an array
2 let family = ["Jóse", "Nicolas", "Lucia", "Enrique"];
3 family[0]; //Jóse
4 family[1]; //Nicolas
5 family[2]; //Lucia
6 family[3]; //Enrique

Adding an element at the end of the array:

1 let family = ["Jóse", "Nicolas", "Lucia", "Enrique"];


2

3 //adding an element at the end of an array


4 family.push("Pablo");

And if you want to add the elements of an existing array to another array
(empty or not), you can use what is known as spread syntax :

1 let myParents = ["EnriqueR", "Susana"];


2 let JoseParents = ["Eduardo", "Graciela"];
3 let family = ["Jóse", "Nicolas", "Lucia", "Enrique"];
4 let all = [...myParents, ...JoseParents, ...family];
5 //[
6 // 'EnriqueR', 'Susana', 'Eduardo', 'Graciela',
7 // 'Jóse', 'Nicolas', 'Lucia', 'Enrique'
8 // ]

Spread syntax is also available for functions to accept an indefinite number


of arguments:

1 function restParams(param1, param2, ...params) {


2 //params is [3, 4, 5]
3 }
4 restParams(1, 2, 3, 4, 5);
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 15

To simply iterate over an array you can use the following for construction:

1 let family = ["Jóse", "Nicolas", "Lucia", "Enrique"];


2 for (let element of family) {
3 console.log("regular for: ", element);
4 }

And in addition we have a set of very useful methods. Let’s see first how
we can iterate over an array using the .forEach method:

1 let family = ["Jóse", "Nicolas", "Lucia", "Enrique"];


2

3 family.forEach(function (value, index, array) {


4 //value is the element being processed
5 //index is the index of the current value
6 //array is the entire array
7 console.log(value, index, array);
8 });

Note that the .forEach method accepts as parameter a function that


accepts three parameters. value which is the element being processed, the
index which is the index of the value being processed and array which is
the array that we are looping. If you are only interested in the elements you
can just do this:

1 let family = ["Jóse", "Nicolas", "Lucia", "Enrique"];


2

3 family.forEach((value) => {
4 //do something with the value here
5 });

Another very interesting method is .filter. Similar to the previous one,


it receives a function with the same parameters. It will return a new array
(shorter or equal than the original) with the elements that evaluates to true.

1 let family = ["Jóse", "Nicolas", "Lucia", "Enrique"];


2

3 const members = family.filter((member) => {


4 return member.length > 5;
5 });
6

7 //members = ['Nicolas', 'Enrique']


CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 16

Note that we are passing an arrow function to the .filter method with
a condition testing the length of each element in the family array. Those
elements whose length is greater than 5 will be part of the returned new
array. Also note that the family array is not changed at all.
The last method we will see is .map. This method receives a function,
same as the previous two methods, and it will return a new array with the
result of applying the function to every element. It will always return an
array of the same length as the one we are processing. As we will see later,
.map is very used in React to add markup to the elements of arrays.

1 let numbers = [1, 2, 3, 4, 5, 6, 7];


2 const doubles = numbers.map((element) => {
3 return element * 2;
4 });
5 //doubles = [2, 4, 6, 8, 10, 12, 14]

Array methods can be combined to produce the desired results. Look at


the example below. We are first applying the .filter function to get an
array only with odd numbers and then we are applying .map to transform it
into an array of even numbers.

1 let numbers = [1, 2, 3, 4, 5, 6, 7];


2 const chain = numbers
3 .filter((element) => {
4 return element % 2 !== 0;
5 }) //[1, 3, 5, 7]
6 .map((element) => {
7 return element * 2;
8 });
9 //chain = [2, 6, 10, 14]

If you have an array with few elements, instead of working with indexes,
there is a very convenient way called destructuring that allows you to assign
each element of the array to named variables. See below:

1 let [one, two, three] = [1, 2, 3];


2 //one = 1
3 //two = 2
4 //three = 3
5

6 //same as the previous


7 let fewNumbers = [1, 2, 3];
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 17

8 [one, two, three] = fewNumbers;


9

10 //and here using spread syntax


11 let [a, b, ...rest] = [1, 2, 3, 4, 5];
12 //a = 1
13 //b = 2
14 //rest = [3, 4, 5]

1.4 Objects
There are several ways to create objects in JavaScript. We will study those
that will be used further in the book when coding in React. The first way
to create objects that we will see is called Object Literal. An object literal
is created wrapping within curly braces a collection of comma-separated
property:value pairs.

1 //an object literal


2 let mi = {
3 name: "Enrique",
4 surname: "Molinari",
5 sports: ["football", "tennis"],
6 address: {
7 street: "San Martin",
8 number: 125,
9 },
10 allSports: function () {
11 console.log(this.sports);
12 },
13 };
14 //this is an empty object
15 let obj = {};

As you can see an object literal can be composed not only of simple
property-value pairs but also for arrays, other objects like address and functions
(called methods). Additionally, since ES6, you can create object literals with
what is called computed property names, like shown below on line 6:

1 let aproperty = "phone";


2 //an object literal with a computed property name
3 let mi = {
4 name: "Enrique",
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 18

5 surname: "Molinari",
6 [aproperty]: "+54 2920 259031"
7 };

Every time the JavaScript interpreter evaluates an object literal a new


object is created. You can access the properties of an object using the dot
notation as the example below shows:

1 console.log(mi.name); //Enrique
2 console.log(mi.sports[0]);//football
3 console.log(mi.address.street);//San Martin
4 console.log(mi.phone);//+54 2920 259031
5 mi.allSports(); //invoke the function and prints the sports array

You can add properties (and remove too) dynamically to an object. In


the example below, on lines 3 and 4 we are adding the properties x and y
(with their corresponding value) to the obj object.

1 let obj = {a: 1, b: 2};


2 //add properties to the obj object
3 obj.x = 3;
4 obj.y = 4;

The spread syntax also works with objects, see below:

1 let obj1 = {
2 a: 1,
3 b: 2,
4 };
5 let obj2 = {
6 c: 3,
7 d: 4,
8 };
9 let obj3 = { ...obj1, ...obj2 };
10 //obj3 = { a: 1, b: 2, c: 3, d: 4 }

And if you want to create an object from some declared variables, you
can do this:

1 let a = 1,
2 b = 2;
3 let obj4 = {
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 19

4 a,
5 b,
6 };
7 //obj4 = { a: 1, b: 2 }

So far we have seen object literal syntax and what you can do with it.
But what if you don’t know how many objects you will need to create? You
need something like a class from class-based languages like Java or C++.
In JavaScript, we have what is called constructor functions. As we will
see later, JavaScript has added classes to the language, but they are just a
syntactic sugar on top of functions.
A constructor function’s name by convention starts with a capital letter.
Lets see how to create and use them:

1 function Book(name, authors, publishedYear) {


2 this.name = name;
3 this.authors = authors;
4 this.publishedYear = publishedYear;
5 this.fullName = function () {
6 return this.name + " by " + this.authors + ". " + publishedYear;
7 };
8 }
9

10 thisBook = new Book("Understanding React",


11 ["Enrique Molinari"], 2021);
12 thisBook.fullName(); //Understanding React by Enrique Molinari. 2021
13

14 archBook = new Book("Coding an Architecture Style",


15 ["Enrique Molinari"], 2020);
16 archBook.fullName(); //Coding an Architecture Style by Enrique Molinari. 2020

As you can see, the function Book looks like a class’s constructor of a
class-based language, in which, in addition, we are able to declare right there
methods like fullName(). We define properties and we initialise them with
the function parameters on lines 2, 3 and 4. On line 5 we define a method.
After that, on lines 10 and 14 we are creating two instances of two different
books and then invoke the fullName() method.

1.4.1 A Prototype-Base Language


Now that we know how to create object literals, constructor functions and
create instances from them, it is time to explain what it means for JavaScript
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 20

to be a prototype-based language. Prototype-based languages are a style


of object oriented programming in which objects are created without creating
classes. That is why they are also called classless languages, in contrast to
class-based object oriented languages (like Smalltalk, Java, C++ and C# to
name a few). In prototype-based languages there are no classes, just objects.
We don’t have that difference between classes and objects. That difference
between a static definition of a blueprint (a class) and their inheritance
relationship (which cannot be changed at runtime) vs the dynamic instantiation
(object creation). And not having the distinction between classes and objects
becomes evident in some situations like the ones we will see next. Defining
methods in constructors functions in the way we did before is not ideal due to
for each instance that we will create we are adding the method fullName()
to it. This can be illustrated with the code below:

1 thisBook = new Book("Understanding React",


2 ["Enrique Molinari"], 2021);
3 archBook = new Book("Coding an Architecture Style",
4 ["Enrique Molinari"], 2020);
5 //printing thisBook
6 //Book {
7 // name: 'Understanding React',
8 // authors: [ 'Enrique Molinari' ],
9 // publishedYear: 2021,
10 // fullName: [Function (anonymous)]
11 //}
12 //printing archBook
13 //Book {
14 // name: 'Coding an Architecture Style',
15 // authors: [ 'Enrique Molinari' ],
16 // publishedYear: 2020,
17 // fullName: [Function (anonymous)]
18 //}

As you can see in the previous example code, the two instances of the Book
constructor function includes, in addition to the property names (and their
values), the function implementation code. Source code is not shared across
the instances like it is in class-based languages. This is an implementation
detail of the language which if you are not aware of it might lead to inefficient
programs. And what about inheritance which is a valuable language resource
used by developers? If there are no classes, do we have inheritance? Yes,
we have. The difference, among others, is that this relation is dynamic,
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 21

meaning that the inheritance relationship in prototype-based languages can


be changed at runtime (as opposed to class-based languages where inheritance
is a static relationship that cannot change at runtime). Here is where we have
to introduce the concept known as prototype.

Each object in JavaScript can have a prototype object, to inherit properties


and methods from it. If you call a property or method in an object and is not
defined there, it will delegate that call to its prototype. Since that prototype
object might have a prototype object too, this delegation will follow until it
is found or fails with an error. This is called a prototype chain.

Each constructor function has access to a special property called prototype,


that can be accessed using dot notation: Book.prototype. And when you
create an instance, you can also access (while in general is not necessary) to
this property using: thisBook.__proto__ or which is the same:
Object.getPrototypeOf(thisBook).
Knowing this we can improve our Book constructor function defined above
in the following way:

1 function Book(name, authors, publishedYear) {


2 this.name = name;
3 this.authors = authors;
4 this.publishedYear = publishedYear;
5 }
6 Book.prototype.fullName = function () {
7 return this.name + " by " + this.authors + ". " + this.publishedYear;
8 };
9

10 thisBook = new Book("Understanding React",


11 ["Enrique Molinari"], 2021);
12 archBook = new Book("Coding an Architecture Style",
13 ["Enrique Molinari"], 2020);
14 //printing thisBook
15 //Book {
16 // name: 'Understanding React',
17 // authors: [ 'Enrique Molinari' ],
18 // publishedYear: 2021
19 //}
20 //printing archBook
21 //Book {
22 // name: 'Coding an Architecture Style',
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 22

23 // authors: [ 'Enrique Molinari' ],


24 // publishedYear: 2020
25 //}

In the example above we have defined the method fullName() in the


prototype of the constructor function (line 6). After that, on lines 10 and 12
we are creating two instances and then printing them. Now as you can see the
fullName() method is not there because it now belongs to their prototype
object, shared by the two Book instances: thisBook and archBook. So, what
happen if we execute the following statement:

1 thisBook.fullName();

JavaScript will try first to find the fullName() method in the thisBook
instance. As it is not defined there, JavaScript will then look in their
prototype object and because it’s there it will be called.
Every prototype chain will end up pointing to Object.prototype. So, if
you execute the following:

1 thisBook.valueOf();

JavaScript will try to find the method valueOf() in the thisBook instance.
Then on their prototype and finally on the prototype object of their prototype,
which is Object.prototype. The method is there, so it is called.
Let’s now create a basic inheritance example. We are going to create a
new EBook constructor function that will inherit from Book.

1 function EBook(filesize, name, authors, publishedYear) {


2 Book.call(this, name, authors, publishedYear);
3 this.filesize = filesize;
4 }
5 let eBook = new EBook(2048, "Understanding React", ["Enrique
,→ Molinari"], 2021);
6

7 //printing eBook:
8 //eBook: EBook {
9 // name: 'Understanding React',
10 // authors: [ 'Enrique Molinari' ],
11 // publishedYear: 2021,
12 // filesize: 2048
13 //}
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 23

Above we have defined the EBook constructor function. On line 2 we are


invoking the Book constructor function using the call method which allows
us to set the value of this as the current object. This is, somehow, analogous
to the use of super(...) inside a constructor in a class to instantiate the
parent class and initialise their private members. On line 5 we are creating
an instance of EBook and if we print the instance on the console we can see
that now we have all the properties from the Book constructor functions on
the eBook object. However, we don’t yet have the fullName() method that
was defined in the Book.prototype. To inherit that method in the EBook
instances we have to set the Book.prototype object as the prototype of the
eBook instance. We do that below:

1 Object.setPrototypeOf(eBook, Book.prototype);
2

3 //Another way of doing the same as above is this:


4 //thisEBook.__proto__ = Book.prototype;
5 //However __proto__ is deprecated

Object.setPrototypeOf is a method where the first parameter is the


instance to have its prototype set and the second parameter is the prototype
object to be set.

1.5 Classes
Yes! JavaScript has classes and their syntax is pretty similar to most of the
class-based languages you might know. They were added to the language in
2015 as part of the EcmaScript 6. The thing is that classes in JavaScript are
a syntactic sugar on top of constructor functions and prototype inheritance.
Behind the scenes, everything works like a prototype-based language, even if
you define instances from classes. That is why it is important to understand
the previous sections.
We will implement our Book and EBook constructor functions with prototype
inheritance from the previous section but using classes.

1 class Book {
2 constructor(name, authors, publishedYear) {
3 this.name = name;
4 this.authors = authors;
5 this.publishedYear = publishedYear;
6 }
7
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 24

8 //this method gets added to the Book.prototype


9 fullName() {
10 return this.name + " by " + this.authors + ". " +
,→ this.publishedYear;
11 }
12 }
13

14 class EBook extends Book {


15 constructor(filesize, name, authors, publishedYear) {
16 super(name, authors, publishedYear);
17 this.filesize = filesize;
18 }
19 }

What we did in the above example with classes, the EBook and Book
inheritance relationship is the same as what we did with the EBook and
Book constructor functions in the section before. See the following code that
demonstrate this:

1 let ebook = new EBook(2048, "Understanding React",


,→ ["Enrique, Molinari"], 2021);
2 //Book.prototype is the prototype of the ebook instance
3 console.log(Book.prototype.isPrototypeOf(ebook)); //true
4 //fullName method is found on the prototype
5 console.log(ebook.fullName()); //Understanding React by
,→ Enrique, Molinari. 2021
6 //EBook is a function not a class
7 console.log(typeof EBook); //function

In the example above, we first create an instance of EBook and then on


line 3 we verify that Book.prototype is the prototype of the ebook instance.
This means that the inheritance relationship was implemented as prototypes
just like functions.

1.6 The Multiple Meanings of this


It is important to understand how this works in JavaScript. Its behaviour
depends on where it is used. We have been using this in the examples from
the previous sections in constructor functions and in classes. And we have
not mentioned anything about it because in those examples it behaves just
like you know from class-based languages like Java or C#. However, there
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 25

are some details you should know specially if you use classes for your React
components.
So far, if you use this in constructor functions and classes, and create
the instances using the new keyword, this is bound to the object being
instantiated. However, specifically with constructor functions this won’t work
as expected if you just call the function like in the next example:

1 function Constr(param) {
2 this.param = param;
3 }
4

5 Constr(2); //this is global object window


6 console.log(window.param); //prints 2

Calling the constructor function as we are doing on line 5 will bind this
to the window global object. So, this example works perfectly, but what
it does is probably something you don’t expect. This example will end up
adding the param property to the window object and assigning to it the value
2.
On classes, on the other hand, if you need to assign or pass as an argument
a method, that method will lose the binding of this. Let’s study the next
example:

1 class Person {
2 constructor(name) {
3 this.name = name;
4 }
5 saySomething() {
6 console.log(this.name + " is talking...");
7 }
8 }
9 let enrique = new Person("Enrique");
10 enrique.saySomething(); //Enrique is talking...
11

12 let o = enrique.saySomething; //assigning to a variable


13 o(); //TypeError: Cannot read property 'name' of undefined

In the previous example we are defining a class Person. Then on line


9 we are creating an instance enrique and on line 10 we are invoking the
saySomething() method. When the method is invoked since this is bound
to the object enrique, this.name which was initialised to the "Enrique"
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 26

string will work and prints "Enrique is talking...". However, on line 12 we


are assigning the method to a variable and then using the variable to invoke
the method on line 13. At the invocation of the saySomething() method
(by the o() call), this is undefined, it is not bound to the object enrique.
Then we will get a TypeError message saying that name is not a property of
undefined.
In order to fix this, we have to explicitly bind the value of this as we see
next:

1 class Person {
2 constructor(name) {
3 this.name = name;
4 this.saySomething = this.saySomething.bind(this);
5 }
6 saySomething() {
7 console.log(this.name + " is talking...");
8 }
9 }
10 let enrique = new Person("Enrique");
11 enrique.saySomething(); //Enrique is talking...
12

13 let o = enrique.saySomething; //assigning to a variable


14 o(); //Enrique is talking...

On line 4 above we are explicitly binding the value of this to the saySomething
method. Inside the constructor the value of this is the object being instantiated.
Now, when saySomething is invoked by the o() call, on line 14, it will work as
expected. Another way to fix this is by declaring methods as arrow functions.

1 class Person {
2 constructor(name) {
3 this.name = name;
4 }
5 saySomething = () => {
6 console.log(this.name + " is talking...");
7 }
8 }
9 let enrique = new Person("Enrique");
10 enrique.saySomething(); //Enrique is talking...
11

12 let o = enrique.saySomething; //assigning to a variable


13 o(); //Enrique is talking...
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 27

Defining the method using the arrow function syntax will work because
arrow functions retain the this value of the enclosing lexical scope which in
this case is the class. However, arrow functions in classes will not get added
to the prototype of the object being instantiated, which means, as we have
already discussed, that every instance will have its own copy of the method.

1.7 Modules
The 6th edition of ECMAScript in 2015 has also added the possibility of
defining modules. Before this release there were other options to create
modular JavaScript programs using tools like RequireJS, among many others.
Now we have this functionality supported natively by modern browsers.
Its use is pretty simple. You can define functions, classes, objects, constants
in a JavaScript file and export those that you want other modules to be used.
Additionally, the client module must import those abstractions that it wants
to use. Let’s see some code examples.

1 //this is my complex-module.js module


2

3 export function complexThing() {


4 console.log("a complex thing has been executed...");
5 }
6

7 export let obj = {


8 a: 1,
9 b: 2,
10 };
11

12 export class ASimpleClass {


13 constructor(name) {
14 this.name = name;
15 }
16

17 print() {
18 console.log("printing: ", this.name);
19 }
20 }

In the module complex-module.js before we are exporting a function, an


object and a class. Observe below, we rewrite the complex-module.js using a
different syntax:
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 28

1 //this is my complex-module.js module


2

3 function complexThing() {
4 console.log("a complex thing has been executed...");
5 }
6

7 let obj = {
8 a: 1,
9 b: 2,
10 };
11

12 class ASimpleClass {
13 constructor(name) {
14 this.name = name;
15 }
16

17 print() {
18 console.log("printing: ", this.name);
19 }
20 }
21

22 export { obj, ASimpleClass, complexThing };

Everything can be exported at the end just like we are doing on line
22 above. Of course, you don’t have to export everything from a module,
just those abstractions that represent the public API of your module. Let’s
see below how from a module called main-module.js you can import the
abstraction exported by the complex-module.js.

1 //this is my main-module.js module


2

3 import { complexThing, obj, ASimpleClass } from


,→ "./module/complex-module.mjs";
4

5 //calling the imported function


6 complexThing();
7

8 //printing the imported object


9 console.log(obj);
10

11 //instantiating the imported class


CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 29

12 let o = new ASimpleClass("Enrique");


13 o.print();

As you can see on line 3 we are importing the three abstractions that the
complex-module.js exports. We are able to use the abstractions imported as
if they were declared in the main-module.js file.
There is a common practice to define a default export abstraction from
a module in order that client modules can import those a bit easier. In the
code below, on line 22, we are exporting the class as our default exported
abstraction from the module.

1 //this is my complex-module.js module


2

3 function complexThing() {
4 console.log("a complex thing has been executed...");
5 }
6

7 let obj = {
8 a: 1,
9 b: 2,
10 };
11

12 class ASimpleClass {
13 constructor(name) {
14 this.name = name;
15 }
16

17 print() {
18 console.log("printing: ", this.name);
19 }
20 }
21

22 export default ASimpleClass;


23 export { obj, complexThing };

On line 3 below, note how we are importing the default exported abstraction
with a different name (it is an alternative, but we can use the same name).
Note also that there are no curly braces.

1 //this is my main-module.js module


2
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 30

3 import AClass from "./module/complex-module.mjs";


4 //This line below is the same as the one above
5 //import { default as AClass } from
,→ "./module/complex-module.mjs";
6 import { obj, complexThing } from
,→ "./module/complex-module.mjs";
7

8 let o = new AClass("Enrique");


9 o.print();
10

11 //... more code here

1.8 Single Thread Language


As per the definition about JavaScript we gave at the beginning of this
chapter, we know that JavaScript is a single threaded language. This
means that the execution of a program is one statement at a time. This
might hurt the performance of the program if there are some statements that
take some time to finish. For this reason, JavaScript supports asynchronous
operations. So, how does this work? The JavaScript interpreter can delegate
the execution of some statements to the Browser and continue executing
the program without waiting for them to finish. After the Browser finishes
the execution of a delegated statement, it is returned to the JavaScript
interpreter as callbacks. Among the statements that the interpreter can
delegate to the browser we have events (onClick, onMouseOver, etc), fetch
and XMLHttpRequest (ajax calls), setTimeout, etc.
To handle this, the execution environment of JavaScript includes the
following elements: the call stack, the browser’s Web APIs, a callback queue
and the event loop. Let’s see the following simple example:
1 console.log("starting");
2

3 setTimeout(() => {
4 console.log("callback");
5 }, 1000);
6

7 console.log("finishing");
Below we can see the sequence of tasks to execute of the program above:
1. Statement on line 1 is pushed on to the call stack and executed. "starting"
is printed on the console.
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 31

2. The setTimeout on line 3 is delegated to the browser’s Web API to


be executed. Which basically waits for one second. However execution
of the program continues with the statement of line 7, it does not wait
because the execution was delegated to the browser’s Web API.

3. Statement on line 7 is pushed on to the call stack and executed. "finishing"


is printed on the console.

4. After one second elapsed from the setTimeout function, the callback
arrow function passed as the first argument was then pushed into the
callback queue. Since there are no more statements to be executed on
the call stack, the event loop gets from the top of the callback queue
the arrow function and pushes it into the call stack. Finally it gets
executed. "callback" is printed on the console.
Note that all the callback functions that end up in the callback queue get
executed after the call stack is empty and not before (when the execution of
the program ends with the last statement). To be very clear with this, look
at the example below.

1 console.log("starting");
2

3 setTimeout(() => {
4 console.log("callback");
5 }, 0);
6

7 console.log("finishing");

Note that on line 5 we are passing 0 seconds to the setTimeout function


telling the Web API to not wait to push the callback arrow function into the
callback queue. In any case, the result and the order of the console messages
is the same as the example before: "starting", "finishing", "callback".
We can expect exactly the same behaviour from the example below that
perform an ajax call:

1 console.log("starting");
2 fetch("https://jsonplaceholder.typicode.com/posts/1")
3 .then((response) => response.json())
4 .then((json) => console.log(json));
5 console.log("finishing");

fetch is delegated to the Web API which performs an ajax call. Once
the server respond, the callback arrow functions are pushed into the callback
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 32

queue. The first callback function, on line 3, transforms the response obtained
from the server to json and the next callback function on line 4 prints that
json in the console. Only after printing on the console the text "finishing"
on line 5, is when those callbacks functions are pushed into the call stack and
executed. You can find a more detailed explanation about the JavaScript
interpreter and how it works, in the great talk by Philip Roberts[5].

1.9 The Promise Object and the async/await


Keywords
The Promise Object was introduced in Javascript in ES2015. This object
represents an asynchronous operation that might finish successfully or fail.
See below how to create an instance of a Promise:

1 let p = new Promise(function (resolve, reject) {


2 //function to be executed by the constructor
3 });
4

5 //do something with p

As we can see above, the Promise constructor receives a function, called


executor, that will be invoked by the constructor. The executor function
receives two additional functions as parameters: resolve(value) and reject(reason).
The body of the executor function performs, typically, an asynchronous
operation and finish by calling the resolve(value) function if everything
goes well or reject(reason) otherwise. See how this is done below:

1 let p = new Promise(function (resolve, reject) {


2 //long async operation
3 setTimeout(() => resolve("finished"), 1000);
4 });

In the example above using setTimeout (on line 3) we are simulating an


operation that takes one second to finish. After that operation finishes it will
call the resolve function passing as value the string "finished". What can
we do with that then? The Promise object have the then(handleResolved)
method that receives a function that allows you to work with the parameter
passed when you invoke the resolve function like we do on line 6 below:

1 let p = new Promise(function (resolve, reject) {


2 //long async operation
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 33

3 setTimeout(() => resolve("finished"), 1000);


4 });
5

6 p.then((value) => console.log(value));

As we can see above, on line 6 we are passing the "finished" string as a


parameter named value to the arrow function passed to the then(handleResolved).
Then, that value is just printed ("finished" is printed on the console).
What is important to note here is that the handleResolved function passed
to the then(...) method is executed only once the promise is resolved.

Suppose that now, something goes wrong with the executor and the
reject function is invoked. Then, it is possible to handle that in the following
way:

1 let p = new Promise(function (resolve, reject) {


2 //long async operation
3 setTimeout(() => reject("can't be done..."), 1000);
4 });
5

6 p.then((value) => console.log("success: " + value))


7 .catch((value) => console.log(value));

Note that on line 3 now we are calling the reject(reason) function


passing the reason value as the string "can't be done". Then, on line 7
note that now we are using the catch, which is the one that will be invoked
in this case.

During this book, and usually in React we don’t write promises, but we
use them frequently. By using the fetch method to retrieve data from an
external API, we have to deal with a promise. Look at the example below:

1 function fetchPost() {
2 fetch("https://jsonplaceholder.typicode.com/posts/1")
3 .then((response) => response.json())
4 .then((json) => console.log(json));
5 }
6

7 fetchPost();

As you can see on line 2 above, the fetch method returns a promise,
which allows us to call the then method on it, to work with the response
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 34

data.

Another way to deal with promises is by using the async and await
keywords. These keywords were added to Javascript on ES2017 allowing
us to write asynchronous code in a synchronous way. Let’s rewrite one of
our previous examples to take advantage of these keywords. First, we will
create a function that returns a promise. Functions that return promises are
(usually) asynchronous:

1 function thePromise() {
2 return new Promise(function (resolve, reject) {
3 //long async operation
4 setTimeout(() => resolve("finished"), 1000);
5 });
6 }

See below how we can invoke the thePromise() function:

1 async function testingKeywords() {


2 console.log("before");
3 const data = await thePromise();
4 console.log("after");
5 console.log(data);
6 }
7

8 testingKeywords();

First note that we have to wrap the calling code in a function. And
that function must be declared async (see line 1). Then, we use the await
keyword before calling the thePromise() function, on line 3. data (the right
hand side of the assignment on line 3) is actually the "finish" string, and
not the promise returned by thePromise() function. Why? because the
execution of the code inside the async function is paused until the promise
is resolved (or rejected). Messages inside the async function are printed on
the console in the same order as the order of the written statements. That
is why we can say that using these keywords allow us to write asynchronous
code that reads like synchronous.

Now we are going to rewrite the fetchPost() function to use these new
keywords. See below:
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 35

1 async function fetchPost() {


2 let data = await
,→ fetch("https://jsonplaceholder.typicode.com/posts/1");
3 data = await data.json();
4 console.log(data);
5 }
6

7 fetchPost();
As you can see, again, we are declaring the function async, and in this case
the fetch call on line 2, is prepended with the await keyword. Prepending
the await keyword to the fetch function means that instead of returning a
Promise object, it returns (if the promises resolve, you can use a try/catch
block to handle errors) a Response object. That allows us to call on line
3 directly to the json() method of the Response object. As that method
returns a Promise, we also prepend the sentence with the await keyword,
giving us a Javascript object that is finally printed on the console.

It is important to note that await can only be used inside an async


function. Let’s see below how to handle errors in an async/await function:
1 async function fetchPost() {
2 try {
3 let data = await
,→ fetch("https://jsonplaceholder.typicode.com/posts/1");
4 data = await data.json();
5 console.log(data);
6 } catch(error) {
7 //handle the error here
8 }
9 }
10

11 fetchPost();
And finally, async functions returns always a Promise. Suppose the
example below where we have an async function that returns the json response
from a fetch request. And with that, we would like to print it on the console.
If we do it like below (see line 6), we will get printed Promise { <pending> }
on the console.
1 async function fetchPost() {
2 let data = await
,→ fetch("https://jsonplaceholder.typicode.com/posts/1");
CHAPTER 1. ESSENTIAL JAVASCRIPT CONCEPTS 36

3 return await data.json();


4 }
5

6 console.log(fetchPost());

Instead, we have to write:

1 fetchPost()
2 .then((data) => console.log(data));

You might also like