React Course
React Course
I. Introduction
Web pages are text files that allow the browser to display rich interfaces to the user:
- To visualize web pages, we need a browser (Chrome, Firefox, Opera, Internet Explorer, etc.).
- To write web pages, we need a text editor (visual studio code, notepad, atom, sublime text, etc.)
- HyperText Markup Language (HTML): indicates to the browser what elements should be
included in the webpage (and in what order).
- Cascading Style Sheets (CSS): indicates how each HTML element should be styled.
- JavaScript (JS): manipulates HTML elements programmatically on the browser in response to
actions by the end user.
Web pages can be served remotely by a web server or saved locally on the user device.
- Client-side rendered pages : the page content is created directly in the browser using JavaScript.
All logic, data fetching, templating and routing are handled on the client rather than the server.
- Server-Side Rendered pages : the page content is created on the web server, then served to the
client. Such pages, are written using languages that run on the server such as PHP, Java and ASP.
- Server-Side + Client-side rendered pages : the page content is created on the server and in the
browser.
1
II. HTML
Reference: https://www.w3schools.com/html/html_intro.asp
1. Introduction
What is HTML?
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
</body>
</html>
Example Explained
- The <!DOCTYPE html> declaration defines that this document is an HTML5 document
- The <html> element is the root element of an HTML page
- The <head> element contains meta information about the HTML page
- The <title> element specifies a title for the HTML page (which is shown in the browser's title
bar or in the page's tab)
- The <body> element defines the document's body, and is a container for all the visible contents,
such as headings, paragraphs, images, hyperlinks, tables, lists, etc.
- The <h1> element defines a large heading
- The <p> element defines a paragraph
2
2. Element syntax
An HTML element is defined by a start tag, some attributes, some content, and an end tag. The HTML
element is everything from the start tag to the end tag:
Example 1:
Element:
Example 2:
Element:
3
3. Empty elements
Some HTML elements have no content. These elements are called empty elements or void elements.
Empty elements do not have an end tag!
HTML elements can be nested (this means that elements can contain other elements).
The following example contains 7 HTML elements (<html>, <body>, <h1>, <p>, <p>, <strong> and <br>):
<!DOCTYPE html>
<html>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
<p title="introduction" style="color:red;">
This is a short <strong>paragraph</strong> with <br>a line break.
</p>
</body>
</html>
5. Attributes
Examples:
- The <a> tag defines a hyperlink. The href attribute specifies the URL of the page the link goes to.
- The <img> tag is used to embed an image in an HTML page. The src attribute specifies the path
to the image to be displayed. The width and height attributes specify the width and height of
the image (in pixels). The alt attribute specifies an alternate text for an image, if the image for
some reason cannot be displayed.
4
6. Comments
HTML comments are not displayed in the browser, but they can help document your HTML source code.
Comments can be added anywhere to the HTML source code by using the following syntax:
Example:
<p>This is a paragraph.</p>
<!-- <p>This is another paragraph </p> -->
1. Headings
<h1> defines the most important heading. <h6> defines the least important heading.
2. Paragraphs
Browsers automatically add a single blank line before and after each <p> element.
3. Links
The <a> tag defines a hyperlink, which is used to link from one page to another.
The most important attribute of the <a> element is the href attribute, which indicates the link's
destination.
Example:
5
4. Images
Images are not technically inserted into a web page; images are linked to web pages. The <img> tag
creates a holding space for the referenced image.
alt - Specifies an alternate text for the image, if the image for some reason cannot be displayed
Note: Also, always specify the width and height of an image. If width and height are not specified, the
page might flicker while the image loads.
Example:
5. Tables
An HTML table consists of one <table> element and one or more <tr>, <th>, and <td> elements.
The <tr> element defines a table row, the <th> element defines a table header, and the <td> element
defines a table cell.
An HTML table may also include <caption>, <colgroup>, <thead>, <tfoot>, and <tbody> elements.
Example: Result:
<table border="1">
<tr>
<th>Month</th>
<th>Savings</th>
</tr>
<tr>
<td>January</td>
<td>$100</td>
</tr>
<tr>
<td>February</td>
<td>$80</td>
</tr>
</table>
6
6. Inputs
The <input> tag specifies an input field where the user can enter data.
The <input> element can be displayed in several ways, depending on the type attribute.
Example: Result:
<label>First name:</label>
<input type="text"
name="fname"><br><br>
<label>Last name:</label>
<input type="text"
name="lname"><br><br>
7. Textareas
The <textarea> element is often used in a form, to collect user inputs like comments or reviews.
A text area can hold an unlimited number of characters, and the text renders in a fixed-width font
(usually Courier).
The size of a text area is specified by the cols and rows attributes (or with CSS).
The name attribute is needed to reference the form data after the form is submitted (if you omit
the name attribute, no data from the text area will be submitted).
7
Example:
<label>Review of W3Schools:</label><br>
<textarea name="w3review" rows="4" cols="50">
At w3schools.com you will learn how to make a website. They offer free
tutorials in all web development technologies.
</textarea>
8. Select
The <select> element is most often used in a form, to collect user input.
The name attribute is needed to reference the form data after the form is submitted (if you omit
the name attribute, no data from the drop-down list will be submitted).
The <option> tags inside the <select> element define the available options in the drop-down list.
Example: Result
<label>Choose a car:</label>
<select name="cars">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="opel" selected>Opel</option>
<option value="audi">Audi</option>
</select>
9. Buttons
Inside a <button> element you can put text (and tags like <i>, <b>, <strong>, <br>, <img>, etc.). That is
not possible with a button created with the <input> element!
8
Example:
10. Forms
The <form> tag is used to create an HTML form for user input.
The <form> element can contain one or more of the following form elements:
<input> <optgroup>
<textarea> <fieldset>
<button> <label>
<select> <output>
<option>
Example: Result:
<form action="/action_page.php">
<label>First name:</label>
<input type="text" name="fname" value="ali"><br>
<label>Last name:</label>
<input type="text" name="lname" value="dridi"><br>
<input type="submit" value="Submit">
</form>
Form attributes:
11. Lists
The <ol> tag defines an ordered list. An ordered list can be numerical or alphabetical.
9
Example: Result:
<p>Ordered list:</p>
<ol>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ol>
<p>Unordered list:</p>
<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
12. Division
The <div> tag is used as a container for HTML elements - which is then styled with CSS or manipulated
with JavaScript.
Note: By default, browsers always place a line break before and after the <div> element.
Example 1:
<div style="border: 5px solid red; background-color: lightblue; text-align: center;"
<h2>This is a heading in a div element</h2>
<p>This is some text in a div element.</p>
</div>
Result:
10
Instead of styling the <div> element using the style attribute, we can create a CSS class and style the
<div> according to this CSS class using the class attribute.
Example:
<style>
.myDiv {
border: 5px solid red;
background-color: lightblue;
text-align: center;
}
</style>
<div class="myDiv">
<h2>This is a heading in a div element.</h2>
<p>This is some text in a div element.</p>
</div>
<!DOCTYPE html>
<html>
<head>
<title>Développement WEB</title>
</head>
<body>
<h1 style="color: blue; text-align: center;">Le code CSS</h1>
<p>Comment utiliser le code CSS dans l’attribut d’un élément HTML</p>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Développement WEB</title>
<style>
h1 {
color: blue;
text-align: center;
}
11
p {
font-family: verdana;
font-size: 20px;
}
</style>
</head>
<body>
<h1>Le code CSS</h1>
<p>Comment utiliser le code CSS dans une page HTML</p>
</body>
</html>
File mystyle.css
h1 {
color: blue;
text-align: center;
}
p {
font-family: verdana;
font-size: 20px;
}
File index.html
<!DOCTYPE html>
<html>
<head>
<title>Développement WEB</title>
<link rel="stylesheet" href="assets/css/mystyle.css">
</head>
<body>
<h1>Le code CSS</h1>
<p>Comment utiliser les fichiers CSS dans une page HTML</p>
</body>
</html>
V. Javascript
The Javascript code can be written in 3 places :
12
Example 1 : Javascript code in the « onClick » attribute of an HTML element
<!DOCTYPE html>
<html>
<head>
<title>Développement WEB</title>
</head>
<body>
<h1>Le code JavaScript </h1>
<button type="button" onclick="document.getElementById("demo").innerHTML=Date();">
Click to show Time.
</button>
<p id="demo"></p>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Développement WEB</title>
<script>
function myFunction() {
document.getElementById("demo").innerHTML = Date();
}
</script>
</head>
<body>
<h1>Le code JavaScript </h1>
<button type="button" onclick="myFunction()">Click to show Time.</button>
<p id="demo"></p>
</body>
</html>
File myscript.js
function myFunction() {
document.getElementById("demo").innerHTML = Date();
}
File index.html
<!DOCTYPE html>
<html>
<head>
<title>Développement WEB</title>
<script src="assets/js/myscript.js"></script>
13
</head>
<body>
<h1>Le code JavaScript </h1>
<button type="button" onclick="myFunction()">Click to show Time.</button>
<p id="demo"></p>
</body>
</html>
<!doctype html>
<html lang="en">
<head>
<title>Bootstrap demo</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]
alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/[email protected]
alpha1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<h1 class="rounded text-center bg-primary text-white p-3 m-3">Hello, world!</h1>
</body>
</html>
Result:
14
Chapter 1: About React.js
I. Introduction
What is React?
- React creates a VIRTUAL DOM in memory: Instead of manipulating the browser's DOM directly,
React creates a virtual DOM in memory, where it does all the necessary manipulating, before
making the changes in the browser DOM.
- React only changes what needs to be changed: React finds out what changes have been made,
and changes only what needs to be changed.
React.JS History
- A single-page application is an application that loads a single HTML page (HTML file).
- Any interactions with the page do not require reloading the page.
- React can build a single-page application, and can be used to enhance existing websites.
15
Popular Stacks:
- Node.js and React.js are the two most common web technologies used by Professional
Developers and those learning to code.
- Angular is used more by Professional Developers than those learning to code (23% vs 10%)
- ASP.NET (18.6%) and ASP.NET Core (14.9%) are widely used.
Babel:
Bundlers:
- Bundlers take JavaScript and CSS code written as separate modules, and combine them together
into a few files better optimized for the browsers.
- Some bundlers commonly used in React applications include Webpack and Browserify.
Package Managers:
- Package managers are tools that allow you to manage dependencies in your project
- npm and Yarn are two package managers commonly used in React applications. Both of them
are clients for the same npm package registry at https://registry.npmjs.org
- JSX is a syntax extension to JavaScript that lets you write HTML-like markup inside a JavaScript
file JSX allows us to put markup into JavaScript
- It is similar to HTML, but it has full power of JavaScript.
- JSX gets compiled into React.createElement() calls which return plain JavaScript objects called
“React elements”.
- Example:
<h1>Hello, world</h1>
17
Elements:
Components:
- React components are small, reusable pieces of code that return a React element to be rendered
to the page.
- The simplest React component is a JavaScript function that returns a React element:
function Welcome() {
return <h1>Hello, world</h1>;
}
- The DOM represents the web page as a tree structure. Any piece of HTML that we write is added
as a node, to this tree.
- With JavaScript, we can access any of these nodes (HTML elements) and update them. This
means that the DOM enables JavaScript to access and modify the web page easily.
- The root node of the DOM is “document”. JavaScript can access this variable in the browser.
- Any HTML tag you write is going to be a direct or indirect child of the root node “document”.
III. Setup
Requirements:
- Install Node.js : we need npm (node package manager) to install dependencies (required
libraries). Available at: https://nodejs.org/
- Install text editor: Visual Studio Code is recommended
18
Create a new React application called “my-app” :
Project structure:
19
“index.html” is the single page of the application
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
The div element having id=root is the container of the React application
“index.js” is the entry point of the application => JavaScript code of this file will be executed on loading
the html page in the browser.
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
reportWebVitals();
import React from 'react';
import ReactDOM from 'react-dom/client';
20
Babel will convert the JSX syntax into a simple JavaScript code:
React doesn’t require using JSX, but it is practical when creating UI inside the JavaScript code easy to
convert html elements into React elements.
JSX and React are two separate things: JSX is a syntax extension, while React is a JavaScript library.
We can declare a variable called name and then use it inside JSX by wrapping it in curly braces:
You can put any valid JavaScript expression inside the curly braces in JSX. For example, 2 + 2, x + y, and
formatName(user) are all valid JavaScript expressions.
21
Example: embed the result of calling a JavaScript function into an <h1> element
function formatName(user) {
return "******" + user + "******";
}
const element = (
<h1>
Hello {formatName("Ali Dridi")}
</h1>
);
Don’t put quotes around curly braces when embedding a JavaScript expression in an attribute. You
should either use quotes (for string values) or curly braces (for expressions), but not both in the same
attribute.
Since JSX is closer to JavaScript than to HTML, React elements use camelCase naming convention
instead of HTML attribute names. For example: tabindex attribute becomes tabIndex.
JSX attributes can’t contain dashes or be reserved words like class: class becomes className in JSX
For historical reasons, aria-* and data-* attributes are written as in HTML with dashes.
Unlike HTML elements, empty React elements must be closed. If a tag is empty, you may close it
immediately with />
Examples:
Example 1: elements wrapped into a div Example 2: elements wrapped into a fragment
let element = ( let element = (
<div> <>
<h1>Hello, world!</h1> <h1>Hello, world!</h1>
<p>This is a simple paragraph</p> <p>This is a simple paragraph</p>
<p>Another paragraph</p> <p>Another paragraph</p>
</div> </>
); );
22
<></> is a shorter syntax for <React.Fragment></React.Fragment>
<></> creates a fragment. Fragments let you group a list of children without adding extra nodes to the
DOM (see https://reactjs.org/docs/fragments.htm ).
Exercise 1:
a) Example 1
<h1>Hello, world!</h1>
<p>This is a simple paragraph.</p>
<p>Another paragraph.</p>
b) Example 2
<h1>Hello, world!</h1>
<p>This is a paragraph with two lines:<br>
-Line 1<br>
-Line 2</p>
c) Example 3
d) Example 4
e) Example 5
<div class="intro">
<h1>Welcome to my website!</h1>
</div>
<p class="summary">
You can find my thoughts here.
<br><br>
<b>And <i>pictures</b></i> of scientists!
</p>
Useful links:
a) Create a new React application called “tp1”. What are the suggested commands when the
application is created ?
b) Open the new application using VS Code (or any other editor of your choice)
c) Start the server
d) Delete all files in the “src” folder except “index.js”
e) Delete useless statements from “index.js”, create a simple JSX element and render it into the
page.
f) Add bootstrap links to the html file “public/index.html”
g) Convert the following HTML element into a JSX element and render it into the page
<h1 class="rounded text-center bg-primary text-white p-3 m-3">
Hello, world!
</h1>
h) Convert the HTML markup of the following Bootstrap Navbar into a JSX element and render it
into the page:
j) Convert the item into JSX element and display it 4 times on a bootstrap row
24
V. Components
Useful links :
- https://reactjs.org/docs/components-and-props.html
- https://reactjs.org/docs/react-component.html
1. Principle
Components let you split the UI into independent, reusable pieces, and think about each piece in
isolation.
Components accept arbitrary inputs (called “props”) and return React elements describing what should
appear on the screen.
React lets you define components as classes or functions. Components defined as classes currently
provide more features.
function Welcome() {
return <h1>Hello World!</h1>;
}
Import Component :
25
Render user-defined components :
function Welcome() {
return <h1>Hello World!</h1>;
}
- When an element type starts with a lowercase letter, it refers to a built-in component like <div>
or <span>
- User-Defined Components Must Be Capitalized
- Types that start with a capital letter like <Hello /> correspond to a component defined or
imported in a JavaScript file.
Example:
Wrong Correct
import React from 'react'; import React from 'react';
// Wrong! // Correct!
function hello() { function Hello() {
return <div>Hello World</div>; return <div>Hello World</div>;
} }
3. Props
React components use “props” (which stands for properties) to communicate with each other. Every
parent component can pass some information to its child components by giving them props.
Props are the information that you pass to a JSX tag. For example, className, src, alt, width, and height
are some of the props you can pass to an <img>.
When React sees an element representing a user-defined component, it passes JSX attributes to this
component as a single object (called “props”).
26
“props” can be the JSX attributes or the object that allows the component to read JSX attributes!
A single JSX attribute is a prop
function Article(props) {
return (
<div className="m-2 p-2 border rounded">
<h1>{props.title}</h1>
<p>{props.content}</p>
<p className="text-muted">{props.date}</p>
</div>
);
}
const element = (
<>
<h1>Hello World</h1>
<Article
title="Computers"
content="Computers are very useful"
date="10/02/2023"
/>
<Article2
title="Internet"
content="Internet has a good speed"
date="23/01/2023"
/>
</>
);
27
Props are Read-Only: Whether you declare a component as a function or a class, it must never modify
its own props.
function Welcome(props) {
Components are reusable: you can create components into different files
When you write a default import, you can put any name you want after import. For example, you could
write import Something from './button.js' and it provides the same default export.
With named imports, the name has to match on both sides. That’s why they are called named imports!
Default export:
28
Named export:
Default import:
Named import:
5. Lifecycle
In applications with many components, it’s very important to free up resources taken by the
components when they are destroyed.
Mounting
These methods are called in the following order when an instance of a component is being created and
inserted into the DOM:
- constructor()
- render()
- componentDidMount()
29
Updating
An update can be caused by changes to props or state. These methods are called in the following order
when a component is being re-rendered:
- render()
- componentDidUpdate()
Unmounting
This method is called when a component is being removed from the DOM:
- componentWillUnmount()
We can declare special methods on the component class to run some code when a component mounts
and unmounts:
constructor() {
}
componentDidMount() {
}
componentWillUnmount() {
}
render() {
return (
<div>
<h1>Hello, world!</h1>
</div>
);
}
}
The constructor for a class component is called before the component is mounted. When implementing
the constructor for a class component, you should call super(props) before any other statement.
Otherwise, this.props will be undefined in the constructor.
30
Example:
render() {
return (
<div>
<span className="border rounded m-2 p-2">{this.props.count}</span>
</div>
);
}
}
VI. ES6
Class overview
The body of a class is the part that is in curly brackets {}. This is where you define class properties (fields
and methods).
Fields can be declared with or without a default value. Fields without default values default to
undefined.
class Rectangle {
height = 0;
width;
info() {
console.log("height: " + this.height);
console.log("width: " + this.width);
console.log("color: " + this.color);
}
}
31
VII. Handling Events
Handling events with React elements is very similar to handling events on DOM elements. There are
some syntax differences:
Example:
In HTML In React
<button onclick="activateLasers()"> <button onClick={activateLasers}>
Activate Lasers Activate Lasers
</button> </button>
Another difference is that you cannot return false to prevent default behavior in React. You must call
preventDefault explicitly. For example, with plain HTML, to prevent the default form behavior of
submitting, you can write:
function Form() {
function handleSubmit(e) {
e.preventDefault();
console.log('You clicked submit.');
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}
Note: the parameter e of handleSubmit is a synthetic event and is passed automatically to the function.
To use callback functions in class components we need to follow one or several steps:
Step 1 : define callback functions and pass them to React events (React events are JSX attributes)
render() {
return (
<div>
<button type="button" onClick={this.addItem}>+</button>
<span className="m-2 p-2">{this.props.count}</span>
</div>
);
}
}
Step 2: to increase/decrease the number of items, we need to define a field and to initialize it in the
constructor. To read the props in the constructor, we should pass it to the constructor and to the base
class.
addItem() {
console.log("Add Item Clicked ...");
}
render() {
return (
<div>
<button type="button" onClick={this.addItem}>+</button>
<span className="m-2 p-2">{this.count}</span>
</div>
);
}
}
By default, “this” keyword is undefined in the callback methods of class components. The callback
function cannot use global fields.
33
Step 3: Bind “this” reference to the callback function in the constructor.
addItem() {
this.count++;
console.log("Add - Total Items: " + this.count);
}
render() {
return (
<div>
<button type="button" onClick={this.addItem}>+</button>
<span className="m-2 p-2">{this.count}</span>
</div>
);
}
}
When we click on the button, “count” will increase by 1 and the new value will be shown on the
console. But the count in the span will not change.
State:
To update the count in the span we need to use this.state which is the component memory:
34
Step 4: Initialize this.state in the constructor, display this.state.property_name in the UI and update
the UI by calling this.setState
addItem() {
//this.count++;
let newCount = this.state.count + 1;
this.setState({count: newCount});
console.log("Add - Total Items: " + newCount);
}
render() {
return (
<div>
<button type="button" onClick={this.addItem}>+</button>
<span className="m-2 p-2">{this.state.count}</span>
</div>
);
}
}
Exercise 1:
a. Create a new class component called “CartItem”. This component requires 3 attributes: name
(the product name), price (the unit price) and count (the number of units). It renders the
following element:
b. Create 2 callback functions: addItem and subItem. addItem increases the number of units by 1.
subItem decreases the number of units by 1 only if the number of units is greater than 1. These
functions should update the number of units and the total price of the product.
35
Hooks: (see https://reactjs.org/docs/hooks-intro.html)
Hooks are a new addition in React 16.8. They let you use state and other React features in function
components without writing a class.
“useState” function is a React Hook that lets you add a state variable to your function component.
Syntax:
Call useState at the top level of your component to declare a state variable.
function MyComponent() {
const [age, setAge] = useState(28);
const [name, setName] = useState('Taylor');
const [todos, setTodos] = useState(() => createTodos());
// ...
a. State initialization:
b. State update:
c. State display:
36
Exercise 2:
a. Create a new function component called “CartItem2”. This component requires 3 attributes:
name (the product name), price (the unit price) and count (the number of units). It renders the
following element:
b. Create 2 callback functions: addItem and subItem. addItem increases the number of units by 1.
subItem decreases the number of units by 1 only if the number of units is greater than 1. These
functions should update the number of units and the total price of the product.
37
TP3
Useful link: https://reactjs.org/docs/forms.html
1. Create a new Function Component called “ContactForm” that renders the following element:
3. Create a callback function for the Submit button called submitForm. This function checks that all
the required fields are filled and displays error messages below missing fields. It uses the created
states.
4. Create a callback function for the Clear button called clearForm. This function clears all the
fields, sets the subject to the first value and deletes any error message.
38
VIII. Client Side Routing
1. Introduction
In traditional websites, the browser requests a document from a web server, downloads and evaluates
CSS and JavaScript assets, and renders the HTML sent from the server. When the user clicks a link, it
starts the process all over again for a new page.
Client side routing allows your app to update the URL from a link click without making another request
for another document from the server. Instead, your app can immediately render some new UI and
make data requests with fetch to update the page with new information.
This enables faster user experiences because the browser doesn't need to request an entirely
new document or re-evaluate CSS and JavaScript assets for the next page.
2. React Router
See: https://reactrouter.com/en/main/start/overview
React Router is the standard library for client side routing with react:
- Once set up, you'll be able to navigate between multiple components within react, synchronized
to changing URL paths
- It works with the HTML5 history, allowing you to use the forward/backward arrows in the
browser
39
Install React Router:
npm i react-router-dom
Client side routing is enabled by creating a Router and linking to pages with "Link" elements.
Rendering a <Link> allows the user to change the URL when they click it. React Router will prevent the
browser's default behavior and tell the history to push a new entry into the history stack. The location
changes and the new matches will render.
function App() {
return (
<BrowserRouter>
<Routes>
<Navbar />
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
<Route path="/contact" element={<Contact />} />
<Route path="*" element={<NoPage />} />
<Footer />
</Routes>
</BrowserRouter>
);
}
40
Example 2: Use <Link> instead of <a>
See: https://www.tutorialspoint.com/restful/restful_web_services_tutorial.pdf
REST stands for REpresentational State Transfer. It is a web architecture that uses HTTP Protocol for
data communication.
REST architecture allows the client to perform CRUD (create, read, update and delete) operation on
data (called resources) using different HTTP methods.
In REST architecture, a REST Server simply provides access to resources and the REST client accesses and
consumes the resources. Each resource is identified by URIs (called endpoints).
REST uses various representations to represent a resource like JSON, Text and XML. JSON is now the
most popular format being used in Web Services.
HTTP Methods
The following HTTP methods are most commonly used in a REST based architecture.
example.com/products
To allow the React app to perform CRUD operation using HTTP requests, we need a REST API (also called
Web API):
- Create a web API using any backend technology: node.js, Laravel, ASP.NET, SpringBoot, etc.
- Use an existing REST API: https://openlibrary.org/developers/api,
https://developers.google.com/books/
- Use a fake REST API on a local server: https://github.com/typicode/json-server
3. JSON Server
JSON Server is a REST server that can be installed locally using npm. It offers a REST API with any
number of endpoints.
Install:
npm i -g json-server
{
"messages": [
],
"posts": [
{ "id": 1, "title": "json-server", "author": "typicode" }
],
"comments": [
{ "id": 1, "body": "some comment", "postId": 1 }
],
"profile": { "name": "typicode" }
}
42
Start the server (from command prompt terminal) :
- If you make POST, PUT, PATCH or DELETE requests, changes will be saved to db.json file
- A POST, PUT or PATCH request should include a Content-Type: application/json header
to use the JSON in the request body. Otherwise it will return a 2XX status code, but without
changes being made to the data.
Routes
Based on the previous db.json file, here are all the default routes:
GET /posts
GET /posts/1
POST /posts
PUT /posts/1
PATCH /posts/1
DELETE /posts/1
Other useful functionalities: Filter, Paginate, Sort, Slice, Operators, Search, etc.
To make network requests, we need an HTTP Client in the browser. The most famous clients are:
Fetch API
See : https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
Fetch is a built-in API, so we don't have to install or import anything. It's available in all modern
browsers.
43
A basic fetch request is really simple to set up. Have a look at the following code:
fetch('http://example.com/messages')
.then((response) => response.json())
.then((data) => console.log(data));
Here we are fetching a JSON file across the network and printing it to the console. The simplest use
of fetch() takes one argument — the path to the resource you want to fetch — and does not directly
return the JSON response body but instead returns a promise that resolves with a Response object.
The Response object, in turn, does not directly contain the actual JSON response body but is instead a
representation of the entire HTTP response. So, to extract the JSON body content from
the Response object, we use the json() method, which returns a second promise that resolves with the
result of parsing the response body text as JSON.
fetch('http://localhost:3004/messages/')
.then((response) => response.json())
.then((data) => console.log(data));
const data = {
firstname: "Ali",
lastname: "Dridi",
email: "[email protected]",
phone: "97888555",
subject: "Order Status",
message: "Dear Sir, Is there any news regarding my last order?"
};
fetch('http://localhost:3004/messages/', {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache',
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer',
body: JSON.stringify(data)
})
.then((response) => response.json())
.then((data) => console.log(data));
44
Example 3 : PUT Request
const data = {
firstname: "Ali",
lastname: "Dridi",
email: "[email protected]",
phone: "97888555",
subject: "Order Status",
message: "new message"
};
fetch('http://localhost:3004/messages/' + id, {
method: 'PUT', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache',
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer',
body: JSON.stringify(data)
})
.then((response) => response.json())
.then((data) => console.log(data));
fetch('http://localhost:3004/messages/1', {
method: 'DELETE'
})
.then((response) => response.json())
.then((data) => console.log(data));
Axios
See : https://axios-http.com/docs/intro
Axios is a third-party HTTP client library. It can be installed by using a CDN or package manager.
To use Axios from the CDN, we can add the following element to the html file:
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
45
TP4
1. Install React Router in the React App using the following command:
npm i react-router-dom
2. Create the “pages” folder under src and create the following files in the “pages” folder:
a. Layout.js : contains the Navbar and the Footer components of the application
b. Home.js: contains the Home component which renders the home page (route / ). This
page contains <h2>Home Page</h2>
c. Products.js: contains the Products component which renders the products page (route
/products ). This page contains <h2>Products Page</h2>
d. Contact.js: contains the Contact component which renders the contact page (route
/contact ). This page contains <h2>Contact Form</h2> and a contact form.
e. NoPage.js: contains the NoPage component which renders the error page (any route).
This page contains <h2>Undefined Page</h2> and a contact form.
46
3. Create the pages/admin/messages folder and add two files into this folder:
a. List.js: contains the List component which lists the stored messages on the REST server.
b. Details.js: contains the Details component which shows the details of a message on the
REST server.
4. Add Client side routing using <BrowserRouter>, <Routes>, <Route> and <Link> elements. Add
the following routes:
a. path="/" element=<Home />
b. path="/products" element=<Products />
c. path="/contact" element=<Contact />
d. path="/admin/messages" element=<List />
e. path="/admin/messages/details/*" element=<Details />
f. path="*" element=<NoPage />
7. Create the db.json file (anywhere) and add the following data to the file:
{
"messages": [
],
"posts": [
{ "id": 1, "title": "json-server", "author": "typicode" }
],
"comments": [
{ "id": 1, "body": "some comment", "postId": 1 }
],
"profile": { "name": "typicode" }
}
9. When the contact form is submitted, the callback function of the Submit button (called
submitForm) should submit an HTTP POST request to create a new message. Check the log data
on the browser console and check db.json file after submitting the form.
10. Complete the List component. It should read the messages from the JSON server and list them
into a table. Each table row allows the Admin to see the details and to delete a message.
47
11. Complete the Details component. It should display the details of a given message.
48
X. Conditional Rendering
See: https://reactjs.org/docs/conditional-rendering.html
In React, you can create distinct components that encapsulate behavior you need. Then, you can render
only some of them, depending on the state of your application.
Conditional rendering in React works the same way conditions work in JavaScript. Use JavaScript
operators like if or the conditional operator to create elements representing the current state, and let
React update the UI to match them.
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
We’ll create a Greeting component that displays either of these components depending on whether a
user is logged in:
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
Element Variables
You can use variables to store elements. This can help you conditionally render a part of the component
while the rest of the output doesn’t change. Example:
function LoginButton(props) {
return (
<button onClick={props.onClick}>Login</button>
);
}
function LogoutButton(props) {
return (
<button onClick={props.onClick}>Logout</button>
);
}
49
In the example below, we will create a stateful component called LoginControl.
It will render either <LoginButton /> or <LogoutButton /> depending on its current state. It will also
render a <Greeting /> from the previous example:
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
While declaring a variable and using an if statement is a fine way to conditionally render a component,
sometimes you might want to use a shorter syntax. You can inline conditions in JSX, as explained below.
You may embed expressions in JSX by wrapping them in curly braces. This includes the JavaScript logical
&& operator. It can be handy for conditionally including an element:
50
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
It works because in JavaScript, true && expression always evaluates to expression, and false &&
expression always evaluates to false.
Therefore, if the condition is true, the element right after && will appear in the output. If it is false,
React will ignore and skip it.
Note that returning a falsy expression will still cause the element after && to be skipped but will return
the falsy expression. In the example below, <div>0</div> will be returned by the render method.
render() {
const count = 0;
return (
<div>
{count && <h1>Messages: {count}</h1>}
</div>
);
}
Another method for conditionally rendering elements inline is to use the JavaScript conditional operator
condition ? true : false.
51
In the example below, we use it to conditionally render an element.
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
}
In rare cases you might want a component to hide itself even though it was rendered by another
component. To do this return null instead of its render output.
In the example below, the <WarningBanner /> is rendered depending on the value of the prop called
warn. If the value of the prop is false, then the component does not render:
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div>
Warning!
</div>
);
}
function Page() {
const [warning, showWarning] = useState(true);
return (
<div>
<WarningBanner warn={warning} />
<button onClick={() => showWarning(!warning)}>
{warning ? 'Hide' : 'Show'}
</button>
</div>
);
}
52
TP 5
1. Create a new file called “AdminProducts.js” under “src\pages\admin”. This file contains 4
components: AdminProducts, ProductList, CreateProduct and EditProduct.
2. AdminProducts is rendered on the following URL: “/admin/products”.
3. The AdminProducts component renders either a list of products (ProductList component) or a
Create Product form (CreateProduct component) or an Edit Product form (EditProduct):
a. If the user clicks on “List” button, AdminProducts renders ProductList element
b. If the user clicks on “Create” button, AdminProducts renders CreateProduct element
c. If the user clicks on “Edit” button, AdminProducts renders EditProduct element
let ui = null;
if (content === "list") {
ui = (
<ProductList products={listProducts} />
);
}
else if (content === "create") {
ui = (
<CreateProduct />
);
}
return (
<div className="container">
<button type="button" onClick={() => setContent("list")}>List</button>
<button type="button" onClick={() => setContent("create")}>Create</button>
<br />
{ui}
</div>
);
}
function CreateProduct() {
return (
<>
<h2 className="text-center mb-3">Create New Product</h2>
</>
);
}
function EditProduct(props) {
return (
<>
<h2 className="text-center mb-3">Edit Product {props.product.name}</h2>
</>
);
}
53
function ProductList(props) {
return (
<>
<h2 className="text-center mb-3">List Products</h2>
<table className="table">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Price</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{
props.products.map((item, index) => {
return (
<tr key={index}>
<td>{item.name}</td>
<td>{item.description}</td>
<td>{item.price}</td>
<td>
<button type="button" className="me-3">Edit</button>
<button type="button">Delete</button>
</td>
</tr>
);
})
}
</tbody>
</table>
</>
);
}
4. CreateProduct should display a form that allow the admin to create a new product on the REST
server. Complete CreateProduct and create a form with the following fields: Name, Category,
Description, Manufacturer, Price. The form has 2 buttons; the submit button sends an HTTP
POST request to the server to create a new product. The cancel button hides the CreateProduct
and shows the ProductList.
5. ProductList reads the products from the server and shows them into a table. Each product in the
table has 2 buttons, the Edit button hides the ProductList and shows EditProduct component.
The delete button deletes the product from the server and refreshes the list of products.
6. EditProduct should display a form that allow the admin to edit a product on the REST server.
Complete EditProduct and create a form with the following fields: Name, Category, Description,
Manufacturer, Price. The form has 2 buttons; the submit button sends an HTTP POST request to
the server to update the product. The cancel button hides EditProduct and shows ProductList.
54
XI. Controlled and Uncontrolled Components
Uncontrolled components are independent components that maintain their own state and update it
based on user input.
Controlled components rise events to their controller component (the parent component), and the
controller component handles the event:
onDelete() handleDelete()
Uncontrolled Form:
// form validation
if (!user.firstname || !user.lastname) {
console.log("Please provide all the required fields!");
return;
}
return (
<form onSubmit={(event) => handleSubmit(event)}>
<label>First Name:</label>
<input type="text" name="firstname" defaultValue="Ali" />
<label>Last Name:</label>
<input type="text" name="lastname" defaultValue="Dridi" />
<button type="submit">Submit</button>
</form>
);
}
55
Controlled Form:
function handleChange(event) {
setUser({...user, [event.target.name]: event.target.value});
}
function handleSubmit(event) {
event.preventDefault();
// form validation
if (!user.firstname || !user.lastname) {
console.log("Please provide all the required fields!");
return;
}
return (
<form onSubmit={(event) => handleSubmit(event)}>
<label>First Name:</label>
<input type="text" name="firstname"
value={user.firstname}
onChange={(event) => handleChange(event)} />
<label>Last Name:</label>
<input type="text" name="lastname"
value={user.lastname}
onChange={(event) => handleChange(event)} />
<button type="submit">Submit</button>
</form>
);
}
Useful links:
56
Example of Controller/Controlled Components:
function showList() {
setContent(<ProductList showForm={showForm} />);
}
function showForm(product) {
setContent(<ProductForm showList={showList} />);
}
return (
<div className="container my-5">
{content}
</div>
);
}
function ProductList(props) {
return (
<>
<h2 className="text-center mb-3">List Products</h2>
<button type="button"
onClick={() => props.showForm({})}>Create</button>
</>
);
}
function ProductForm(props) {
return (
<>
<h2 className="text-center mb-3">Create New Product</h2>
<button type="button"
onClick={() => props.showList({})}>Cancel</button>
</>
);
}
57
Chapter 2: Hooks
Link: https://react.dev/reference/react
I. Introduction
Hooks are functions that allow function components to have similar capabilities as class components.
You can either use the built-in Hooks or combine them to build your own.
State lets a component “remember” information like user input. For example, a form component can
use state to store the input value, while an image gallery component can use state to store the selected
image index.
2. Context Hooks
Context lets a component receive information from distant parents without passing it as props. For
example, your app’s top-level component can pass the current UI theme to all components below, no
matter how deep.
3. Ref Hooks
Refs let a component hold some information that isn’t used for rendering, like a DOM node or a timeout
ID. Unlike with state, updating a ref does not re-render your component. Refs are an “escape hatch”
from the React paradigm. They are useful when you need to work with non-React systems.
- useRef declares a ref. You can hold any value in it, but most often it’s used to hold a DOM node.
- useImperativeHandle lets you customize the ref exposed by your component. It is rarely used.
58
4. Effect Hooks
Effects let a component connect to and synchronize with external systems. This includes dealing with
network, browser DOM, animations, widgets written using a different UI library, and other non-React
code.
There are two rarely used variations of useEffect with differences in timing:
- useLayoutEffect fires before the browser repaints the screen. You can measure layout here.
- useInsertionEffect fires before React makes changes to the DOM. Libraries can insert dynamic
CSS here.
5. Performance Hooks
A common way to optimize re-rendering performance is to skip unnecessary work. For example, you
can tell React to reuse a cached calculation or to skip a re-render if the data has not changed since the
previous render.
- useTransition marks a state transition as non-blocking and allow other updates to interrupt it.
- useDeferredValue defers updating a non-critical part of the UI and let other parts update first.
6. Other Hooks
These Hooks are mostly useful to library authors and aren’t commonly used in the application code.
- useDebugValue lets you customize the label React DevTools displays for your custom Hook.
- useId lets a component associate a unique ID with itself. Typically used with accessibility APIs.
- useSyncExternalStore lets a component subscribe to an external store.
7. Custom Hooks
https://react.dev/learn/reusing-logic-with-custom-hooks#extracting-your-own-custom-hook-from-a-
component
59
III. useContext
https://www.w3schools.com/react/react_usecontext.asp
It can be used together with useState to share state between deeply nested components more easily
than with useState alone.
return (
<>
<Total count={count} />
<hr />
<Component1 count={count} setCount={setCount} />
</>
);
}
function Total(props) {
return (<h1>Total: {props.count}</h1>);
}
function Component1(props) {
return (
<>
<h1>Component 1</h1>
<Product count={props.count} setCount={props.setCount} >
Product 1
</Product>
<Product count={props.count} setCount={props.setCount} >
Product 2
</Product>
</>
);
}
function Product(props) {
return (
<>
<h1>{props.children}</h1>
<button onClick={() => props.setCount(props.count + 1)}>
Add Product
</button>
</>
);
}
Even though Component1 did not need the state, it had to pass the state along to reach Product. This is
called "prop drilling".
60
2. useContext + useState
return (
<CountContext.Provider value={{count, setCount}}>
<Total />
<hr />
<Component1 />
</CountContext.Provider>
);
}
function Total() {
const data = useContext(CountContext);
return (<h1>Total: {data.count}</h1>);
}
function Component1() {
return (
<>
<h1>Component 1</h1>
<Product >Product 1</Product>
<Product >Product 2</Product>
</>
);
}
function Product(props) {
const data = useContext(CountContext);
return (
<>
<h1>{props.children}</h1>
<button onClick={() => data.setCount(data.count + 1)}>
Add Product
</button>
</>
);
}
61
Example 2 : Parent and child components in different files
Create Context
CountContext.js
import { createContext } from "react";
Home.js
import React, { useState } from "react";
import { Total } from "./Total";
import { Product } from "./Product";
import { CountContext } from "./CountContext";
return (
<CountContext.Provider value={{count, setCount}}>
<Total />
<hr />
<Component1 />
</CountContext.Provider>
);
}
function Component1() {
return (
<>
<h1>Component 1</h1>
<Product >Product 1</Product>
<Product >Product 2</Product>
</>
);
}
Total.js
import React, { useContext } from "react";
import { CountContext } from "./CountContext";
62
Import Context in Child components and use the useContext Hook
Product.js
import React, { useContext } from "react";
import { CountContext } from "./CountContext";
return (
<>
<h1>{props.children}</h1>
<button onClick={() => data.setCount(data.count + 1)}>
Add Product
</button>
</>
);
}
IV. useRef
useRef is a React Hook that lets you reference a value that’s not needed for rendering.
Example:
function Home() {
let countRef = useRef(0);
function handleClick() {
countRef.current = countRef.current + 1;
console.log('You clicked ' + countRef.current + ' times!')
}
return (
<div className="container my-5">
<h2>Home Page</h2>
<button onClick={handleClick}>
Click me!
</button>
</div>
);
}
63
V. useEffect
https://react.dev/reference/react/useEffect
useEffect is a React Hook that lets you synchronize a component with an external system.
useEffect(setup, dependencies?)
Parameters
- setup: The function with your Effect’s logic. Your setup function may also optionally return a
cleanup function. When your component is first added to the DOM, React will run your setup
function. After every re-render with changed dependencies, React will first run the cleanup
function (if you provided it) with the old values, and then run your setup function with the new
values. After your component is removed from the DOM, React will run your cleanup function
one last time.
- optional dependencies: The list of all reactive values referenced inside of the setup code.
Reactive values include props, state, and all the variables and functions declared directly inside
your component body. If your linter is configured for React, it will verify that every reactive value
is correctly specified as a dependency. The list of dependencies must have a constant number of
items and be written inline like [dep1, dep2, dep3]. React will compare each dependency with its
previous value. If you omit this argument, your Effect will re-run after every re-render of the
component.
If you specify the dependencies, your Effect runs after the initial render and after re-renders with
changed dependencies.
useEffect(() => {
// ...
}, [a, b]); // Runs again if a or b are different
64
Example 2 of 3: Passing an empty dependency array
If your Effect truly doesn’t use any reactive values, it will only run after the initial render.
useEffect(() => {
// ...
}, []); // Does not run again
If you pass no dependency array at all, your Effect runs after every single render (and re-render) of your
component.
useEffect(() => {
// ...
}); // Always runs again
65
Chapter 3: Client-side Data storage
- Cookies
- Local Storage
- Session Storage
You can check the stored data on the browser using DevTools
66
I. Cookies
https://www.w3schools.com/js/js_cookies.asp
When a web server has sent a web page to a browser, the connection is shut down, and the server
forgets everything about the user.
Cookies were invented to solve the problem "how to remember information about the user":
- When a user visits a web page, his/her name can be stored in a cookie.
- Next time the user visits the page, the cookie "remembers" his/her name.
When a browser requests a web page from a server, cookies belonging to the page are added to the
request. This way the server gets the necessary data to "remember" information about users.
JavaScript can create, read, and delete cookies with the document.cookie property.
You can also add an expiry date (in UTC time). By default, the cookie is deleted when the browser is
closed:
With a path parameter, you can tell the browser what path the cookie belongs to. By default, the cookie
belongs to the current page.
67
Example:
let x = document.cookie;
The document.cookie property looks like a normal text string. But it is not.
document.cookie will return all cookies in one string much like: cookie1=value1; cookie2=value2;
cookie3=value3;
Even if you write a whole cookie string to document.cookie, when you read it out again, you can only
see the name-value pairs of it.
If you set a new cookie, older cookies are not overwritten. The new cookie is added to
document.cookie, so if you read document.cookie again you will get something like:
If you want to find the value of one specified cookie, you must write a JavaScript function that searches
for the cookie value in the cookie string.
Example:
function getCookieValue(cookieName) {
// document.cookie contains all the cookies with the following format
// cookie1=value1; cookie2=value2; cookie3=value3;
return "";
}
68
3. Change a Cookie with JavaScript
With JavaScript, you can change a cookie the same way as you create it:
Deleting a cookie is very simple. You don't have to specify a cookie value when you delete a cookie.
1. Introduction
The Web Storage API provides mechanisms by which browsers can securely store key/value pairs.
Storage objects are simple key-value stores, similar to objects, but they stay intact through page loads.
The keys and the values are always strings (integer keys will be automatically converted to strings). You
can access these values like an object, or with the Storage.getItem() and Storage.setItem() methods.
- sessionStorage maintains a separate storage area for each given origin that's available for the
duration of the page session (as long as the browser is open, including page reloads and
restores).
- localStorage does the same thing, but persists even when the browser is closed and reopened.
Note: sessionStorage is useless in React application because the application is able to maintain its data
during the page session (single page session)
69
2. Window localStorage
The localStorage object allows you to save key/value pairs in the browser.
localStorage.colorSetting = "#a4509b";
localStorage["colorSetting"] = "#a4509b";
localStorage.setItem("colorSetting", "#a4509b");
if (!localStorage.getItem("bgcolor")) {
localStorage.setItem("bgcolor", "blue");
}
//Remove by name
localStorage.removeItem(key);
const myObject = {
name : "john doe",
age : 32,
gender : "male",
profession : "optician"
}
localStorage.setItem("myObject", JSON.stringify(myObject));
70
TP 6
1) Add a shopping cart item to the navbar. It displays the number of units in the shopping cart and
allows the user to access the shopping cart details (displays the Cart component).
2) Create the Cart component that displays a page with the different products of the shopping cart.
The component allows:
a. Increasing/decreasing the number of products in the shopping cart
b. Removing a product from the shopping cart
c. Displaying the total price of the order
d. Displaying the total price of the different units of a single product
e. Updating the number of units in the shopping cart item of the navbar
3) The application should initialize the shopping cart and save its data in the local storage.
71
72
Chapter 4: Authentication and Authorization
Links:
http://assets.digitalocean.com/books/how-to-code-in-reactjs.pdf
https://www.digitalocean.com/community/tutorials/how-to-add-login-authentication-to-react-
applications
1. Introduction
Web applications are a mix of public and private pages. Public pages are available to anyone, while a
private page requires a user login.
You can use authentication to manage which users are authorized to access which pages.
Authentication verifies the identity of a user or service, and authorization determines their
access rights.
Which Comes First, Authentication or Authorization? As you cannot authorize a user before identifying
him, authentication always comes before authorization.
There are several authentication methods. The principal method is the authentication using JWT (JSON
Web Token):
- After successful authentication, the server sends a JWT to the client app
- The client app saves the JWT on the browser
- The client app includes the JWT to the requests that require the server authorization
- When the server receives a request that requires authorization, the server checks the validity of
the JWT before sending the response.
73
React applications need to :
- handle situations where a user tries to access a private page before they are authenticated
- save the login information (JWT) once users have successfully authenticated
- include the JWT to the requests sent to the server
74
Standard Claim Names:
aud Audience Identifies the recipients that the JWT is intended for.
Expiration Identifies the expiration time on and after which the JWT must not be accepted for
exp
Time processing. The value must be a NumericDate.
Identifies the time on which the JWT will start to be accepted for processing. The
nbf Not Before
value must be a NumericDate.
iat Issued at Identifies the time at which the JWT was issued. The value must be a NumericDate.
jti JWT ID Case-sensitive unique identifier of the token even among different issuers.
Example:
Encoded JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMTIiLCJuYW1lIjoiQWxpI
ERyaWRpIiwiZW1haWwiOiJhbGkuZHJpZGlAZ21haWwuY29tIiwiaWF0IjoxNTE2MjM5MDI
yLCJyb2xlIjoiYWRtaW4iLCJwaG9uZSI6IisyMTY5OTEyMzQ1NiIsImFkZHJlc3MiOiJNb
25hc3RpciwgVHVuaXNpYSJ9.vOnTPPsTUKbB73o2v0DwMh7pV7oTTaiHO37Ssgi2eCs
Decoded JWT:
Header
{
"alg": "HS256",
"typ": "JWT"
}
Payload
{
"sub": "112",
"name": "Ali Dridi",
"email": "[email protected]",
"iat": 1516239022,
"role": "admin",
"phone": "+21699123456",
"address": "Monastir, Tunisia"
}
Signature
HMACSHA256(base64Url(header) + "." + base64Url(payload), mystrongpassword123)
Test at https://jwt.io/
75
3. JSON Server Auth
Link: https://www.npmjs.com/package/json-server-auth
JSON Server Auth is an authentication module for JSON Server. It offers authentication & authorization
flow for your prototyping.
Install
{
"users": []
}
Register
POST /register
POST /signup
POST /users
POST /register
{
"email": "[email protected]",
"password": "bestPassw0rd"
}
The password is encrypted by bcryptjs. The response contains the JWT access token (expiration time of
1 hour) :
201 Created
{
"accessToken": "xxx.xxx.xxx"
}
76
Any other property can be added to the request body:
POST /register
{
"email": "[email protected]",
"password": "bestPassw0rd",
"firstname": "Olivier",
"lastname": "Monge",
"age": 32
}
Update
Any update to an existing user (via PATCH or PUT methods) will go through the same process for email
and password.
Login
POST /login
POST /signin
POST /login
{
"email": "[email protected]",
"password": "bestPassw0rd"
}
The response contains the JWT access token (expiration time of 1 hour) :
200 OK
{
"accessToken": "xxx.xxx.xxx"
}
JWT payload
77
4. Authentication
- Create a token state to store the token value and to check the authentication status
- Create new routes for the Register and Login components
- Share the token state and setToken with the Navbar, Register and Login components
function App() {
const [cartSize, setCartSize] = useState(0)
const [token, setToken] = useState("");
return (
<BrowserRouter>
<Navbar cartSize={cartSize} token={token} setToken={setToken} />
<Routes>
<Route path="/" element={<Home cartSize={cartSize} setCartSize={setCartSize} />} />
<Route path="/products" element={<Products />} />
<Route path="/contact" element={<Contact />} />
<Footer />
</BrowserRouter>
);
}
- Check if the user is authenticated or not. If the user is authenticated, he will be redirected to “/”
- Sends the user details using POST method to JSON Server Auth at /register
- If the authentication is successful, save the token in the App token state
function handleSubmit(event) {
event.preventDefault();
78
// register a new user
fetch("http://localhost:3004/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(user)
})
.then((response) => {
if (!response.ok) {
throw new Error("Network response was not OK");
}
return response.json()
})
.then((data) => {
console.log("Your new JWT is: ", data.accessToken)
props.setToken(data.accessToken)
})
.catch((error) => {
console.error("Error:", error);
});
}
return (
<div className="row">
<div className="col-lg-6 mx-auto">
<h2>Register Form</h2>
...
</div>
</div>
)
}
- Use conditional rendering to add a UL to the Navbar: if the user is authenticated => show drop-
down list, otherwise => show Register/Login buttons
- Add Logout item => Logout allows the application to clear the JWT
79
<ul className="dropdown-menu">
<li><Link className="dropdown-item"
to="/admin/products">Products</Link></li>
<li><Link className="dropdown-item" to="/">Orders</Link></li>
<li><Link className="dropdown-item" to="/">Users</Link></li>
<li><Link className="dropdown-item"
to="/admin/messages">Messages</Link></li>
<li><hr className="dropdown-divider" /></li>
<li><Link className="dropdown-item" to="/#"
onClick={() => props.setToken("")}>Logout</Link></li>
</ul>
</li>
</ul>
)
if (!props.token) {
ul = (
<ul className="navbar-nav">
<li className="nav-item">
<Link className="btn btn-outline-primary me-2"
to="/Auth/Register">Register</Link>
</li>
<li className="nav-item">
<Link className="btn btn-primary" to="/Auth/Login">Login</Link>
</li>
</ul>
)
}
return (
<nav className="navbar navbar-expand-md py-3 mb-3">
<div className="container">
<Link className="navbar-brand" to="/">
Best Shop
</Link>
<button className="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-
expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNavDropdown">
<ul className="navbar-nav flex-grow-1">
<li className="nav-item">
<Link className="nav-link text-dark" to="/">Home</Link>
</li>
<li className="nav-item">
<Link className="nav-link text-dark" to="/products">Products</Link>
</li>
<li className="nav-item">
<Link className="nav-link text-dark" to="/contact">Contact</Link>
</li>
</ul>
{ul}
</div>
</div>
</nav>
);
}
80
TP 7
b. If the user clicks on the Logout item, he will be logged out (token will be deleted)
82
5. Authorization
Link: https://www.npmjs.com/package/json-server-auth
To handle common use cases, JSON Server Auth draws inspiration from Unix filesystem permissions,
especialy the numeric notation.
Similarly to Unix, we then have three digits to match each user type :
For example, 640 means that only the owner can write the resource, logged-in users can read the
resource, and public users cannot access the resource at all.
A user is the owner of a resource if that resource has a userId property that matches his id property.
Example:
// The owner of
{ id: 8, text: 'blabla', userId: 1 }
// is
{ id: 1, email: '[email protected]' }
Private guarded routes will use the JWT sub claim (which equals the user id) to check if the user actually
owns the requested resource, by comparing sub with the userId property.
Except for the actual users collection, where the JWT sub claim must match the id property.
83
Guarded routes
Guarded routes exist at the root and can restrict access to any resource you put after them :
600 User must own the resource to write or read the resource.
Examples
Request Response
POST /664/posts
401 UNAUTHORIZED
{text: 'hello'}
84
Logged-in user with id: 1 does the following requests :
Request Response
GET /600/users/1
200 OK
Authorization: Bearer xxx.xxx.xxx
GET /600/users/23
403 FORBIDDEN
Authorization: Bearer xxx.xxx.xxx
Setup permissions
Of course, you don't want to directly use guarded routes in your requests. We can define permissions in
a configuration file.
{
"users": 600,
"messages": 640,
"products": 644
}
85
b. Client requests
During the authentication step, the client sends his email/password and receives 2 parameters from
JSON Server Auth: “accessToken” (JWT) and “user” (the user data object):
// login
fetch("http://localhost:3004/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(credentials)
})
.then((response) => {
if (response.ok) {
return response.json().then((data) => {
console.log("Your new JWT is: ", data.accessToken)
console.log("User datails: ", data.user)
props.updateTokenAndUser(data.accessToken, data.user)
})
}
//else
return response.text().then((data) => {
console.log("Login Error: ", data)
})
})
.catch((error) => {
console.error("Error:", error);
});
To access a private resource, the client app should include the « Authorization » header with the bearer
token (i.e. bearer JWT).
Example:
fetch("http://localhost:3004/users/" + props.user.id, {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + props.token
}
})
.then((response) => {
if (response.ok) {
return response.json().then((data) => {
console.log("Get Profile - Your data is: ", data)
})
}
//else
return response.text().then((data) => {
console.log("Get Profile Error: ", data)
})
});
86
TP 8
1. Create a configuration file (routes.json) that protects the server resources as follows:
a. users: 600
b. messages: 640
c. products: 644
2. Configure the JSON Server auth to use “routes.json” and the port number 3004:
json-server-auth db.json --port 3004 -r routes.json
4. Create the Profile component that allows the user to see his profile. The endpoint that allows
the user with id=2 to read/update his profile is: http://localhost:3004/users/2
a. Is it possible to read the user profile without adding the JWT to the request header?
87
5. Create the EditProfile component that allows the user to edit his profile or password.
return (
<>
<h2>Profile</h2>
<Link className="btn btn-primary" to="/profile/edit"
role="button">Edit</Link>
</>
)
}
88
export function EditProfile() {
const navigate = useNavigate();
function saveProfile() {
// read new profile data and submit an update request
navigate("/profile")
}
return (
<>
<h2>Edit Profile</h2>
<button className="btn btn-primary me-3"
onClick={() => saveProfile()}>Submit</button>
<Link className="btn btn-secondary" to="/profile"
role="button">Close</Link>
</>
)
}
7. Create the Users component that allows the admin to see the list of registered users.
a. Is it possible to allow the admin to read all the users on the server using the previous
“routes.json” configuration file?
b. Update the permission value of the users resource in “routes.json” and use users: 640.
Restart the JSON Server auth.
c. Add “role=admin” to the admin user in db.json file
d. Update the Navbar of the react app: if the user is admin, display the item “users” in the
dropdown list. This item displays the Users component which displays the list of users on
the server
89
6. Protected Routes
Link: https://www.robinwieruch.de/react-router-private-routes/
Protect the Profile component: check if the user is authenticated inside the protected component itself
function App() {
const [token, setToken] = useState(localStorage.getItem("jwt"));
const [user, setUser] = useState(JSON.parse(localStorage.getItem("user")));
return (
<BrowserRouter>
<Navbar />
<Routes>
...
<Route path="/profile" element={<Profile token={token} user={user} />} />
...
</Routes>
</BrowserRouter>
);
}
function Profile(props) {
if (!props.token) {
return <Navigate to="/login" />
}
return (
<h2>Profile (Protected: authenticated user required)</h2>
)
}
However, this approach does not scale, because we would have to implement the same logic
in every protected route.
In addition, the redirect logic should not reside in the Home component itself but as a best
practice protect it from the outside instead.
Therefore, we will extract the logic into a standalone component (let’s call it ProtectedRoute):
return props.children;
}
90
Then we can use ProtectedRoute as wrapper for the Profile component.
function App() {
...
return (
<BrowserRouter>
<Navbar />
<Routes>
...
<Route path="/profile" element={
<ProtectedRoute token={token} >
<Profile user={user} />
</ProtectedRoute>
} />
...
</Routes>
</BrowserRouter>
);
}
function Profile(props) {
return (
<h2>Profile (Protected: authenticated user required)</h2>
)
}
Let’s reuse this component for other routes which need the same level of protection. For example, the
Dashboard page requires a user to be logged in too:
function App() {
...
return (
<BrowserRouter>
<Navbar />
<Routes>
...
<Route path="/profile" element={
<ProtectedRoute token={token} >
<Profile user={user} />
</ProtectedRoute>
} />
<Route path="/dashboard" element={
<ProtectedRoute token={token} >
<Dashboard user={user} />
</ProtectedRoute>
} />
...
</Routes>
</BrowserRouter>
);
}
A better way of protecting both sibling routes with the same authorization level would be using a Layout
Route which renders the ProtectedRoute component for both nested routes:
91
function ProtectedRoute (props) {
if (!props.token) {
return <Navigate to="/auth/login" />;
}
function App() {
...
return (
<BrowserRouter>
<Navbar />
<Routes>
...
<Route path="/products" element={<Products />} />
<Route element={<ProtectedRoute token={token} />}>
<Route path="profile" element={<Profile user={user} />} />
<Route path="dashboard" element={<Dashboard user={user} />} />
</Route>
<Route path="/contact" element={<Contact />} />
...
</Routes>
</BrowserRouter>
);
}
92
Chapter 5: JavaScript Syntax
I. Objects
Link: https://www.w3schools.com/js/js_objects.asp
The values are written as name:value pairs (name and value separated by a colon).
Object Definition
You can create an object using an object literal. An object literal is a comma-delimited list of zero or
more pairs of property names and associated values of an object, enclosed in curly braces ({})
This code assigns many values (Fiat, 500, white) to a variable named car:
Spaces and line breaks are not important. An object definition can span multiple lines:
const person = {
firstName: "John",
lastName: "Doe",
age: 50,
eyeColor: "blue"
};
Object Properties
93
Accessing Object Properties
objectName.propertyName
or
objectName["propertyName"]
Example:
Object Methods
Objects can also have methods. Methods are actions that can be performed on objects. Methods are
stored in properties as function definitions.
const person = {
firstName: "John",
lastName : "Doe",
id : 5566,
fullName : function() {
return this.firstName + " " + this.lastName;
}
};
In the example above, this is the person object that "owns" the fullName function.
The object literal syntax is not the same as the JavaScript Object Notation (JSON). Although they look
similar, there are differences between them:
- JSON only permits property definition using the "property": value syntax. The property name
must be double-quoted
- JSON object property values can only be strings, numbers, true, false, null, arrays, or another
JSON object. This means JSON cannot express methods or non-plain objects like Date or RegExp.
JSON is a strict subset of the object literal syntax, meaning that every valid JSON text can be parsed
as an object literal, and would likely not cause syntax errors.
94
II. Arrays
1. Description
An array enables storing a collection of multiple items under a single variable name, and has members
for performing common array operations.
Syntax:
Example:
You can also create an array, and then provide the elements:
cars[0] = "Opel";
2. Array Methods
The following table lists the methods that mutate the original array, and the corresponding non-
mutating alternative:
95
Add items
- push
- unshift
- concat: used to merge two or more arrays. This method does not change the existing arrays, but
instead returns a new array.
console.log(array3);
// Expected output: Array ["a", "b", "c", "d", "e", "f"]
- spread operator: The JavaScript spread operator (...) allows us to quickly copy all or part of an
existing array or object into another array or object.
Remove items
- pop
- shift
- splice
- filter: creates a shallow copy (A shallow copy of an object is a copy whose properties share the
same references as those of the source object from which the copy was made) of a portion of a
given array, filtered down to just the elements from the given array that pass the test
implemented by the provided function.
console.log(result);
// Expected output: Array ["exuberant", "destruction", "present"]
- slice: returns a shallow copy of a portion of an array into a new array object selected from start
to end (end not included) where start and end represent the index of items in that array. The
original array will not be modified.
console.log(animals.slice(2));
// Expected output: Array ["camel", "duck", "elephant"]
console.log(animals.slice(2, 4));
// Expected output: Array ["camel", "duck"]
console.log(animals.slice(1, 5));
// Expected output: Array ["bison", "camel", "duck", "elephant"]
96
console.log(animals.slice(-2));
// Expected output: Array ["duck", "elephant"]
console.log(animals.slice(2, -1));
// Expected output: Array ["camel", "duck"]
console.log(animals.slice());
// Expected output: Array ["ant", "bison", "camel", "duck", "elephant"]
Replace items
- splice
- arr[i]
- map: creates a new array populated with the results of calling a provided function on every
element in the calling array.
console.log(map1);
// Expected output: Array [2, 8, 18, 32]
Sort items
- reverse: reverses an array in place and returns the reference to the same array, the first array
element now becoming the last, and the last array element becoming the first.
- sort
- toReversed: this is the copying counterpart of the reverse() method. It returns a new array with
the elements in reversed order.
console.log(reversedItems); // [3, 2, 1]
console.log(items); // [1, 2, 3]
- toSorted: this is the copying version of the sort() method. It returns a new array with the
elements sorted in ascending order.
97
III. JavaScript ES6
https://www.w3schools.com/js/js_es6.asp
98