📄 Page
1
React Understanding The Simplest Practical Guide to Start Coding in React Enrique Pablo Molinari Includes: React Hooks and React Router + Full Stack Authentication & Authorization Flow
📄 Page
2
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.
📄 Page
3
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
📄 Page
4
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
📄 Page
5
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 enrique.molinari@gmail.com to send thoughts, comments or questions about this book, the other or my blog. 5
📄 Page
6
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
📄 Page
7
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
📄 Page
8
Part I Introduction 8
📄 Page
9
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
📄 Page
10
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.
📄 Page
11
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!");
📄 Page
12
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;
📄 Page
13
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:
📄 Page
14
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);
📄 Page
15
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']
📄 Page
16
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];
📄 Page
17
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",
📄 Page
18
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 = {
📄 Page
19
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
📄 Page
20
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,