Académique Documents
Professionnel Documents
Culture Documents
Vous avez fait un très bon choix car React.JS est une des technos les plus
demandées et les plus valorisées dans l'industrie du développement logiciel, et
d'ici la fin de cette formation vous pourrez sans souci candidater pour un
poste de développeur React !
Qui suis-je ?
Je suis Vincent ANDRIEU, votre formateur pour ce cours React. Je ne vais pas
trop m'étendre sur ma présentation car je ne suis pas ici pour parler de moi,
alors je vais me contenter de vous rediriger sur mon site web pour ceux qui
voudraient savoir qui je suis : https://vincentandrieu.fr, mais en résumé, je suis
ingénieur informatique depuis 2008, et je travaille avec React depuis 2018.
Présentation et introduction 1
Pourquoi un "squelette" d'application ?
Nous n'allons pas développer une interface complète de réseau social car notre
but ici est de parcourir toutes les notions nécessaires à l'apprentissage de
react. Si on faisait une interface complète nous aurions 2 inconvénients
majeurs :
Présentation et introduction 2
Le minimum de CSS
Nous ne ferons pas de CSS pendant une bonne partie de la formation,
uniquement du React qui génère du HTML brut, afin de nous focaliser à 100%
sur React sans être pollués par la mise en place de styles qui est assez
chronophage. Une fois les concepts essentiels abordés, nous verrons comment
ajouter le CSS puis nous continuerons de coder react et CSS en parallèle pour
la suite.
C'est parti !
Présentation et introduction 3
Qu'est-ce que React.js et
pourquoi l'utiliser ?
Objectifs
Comprendre
C'est une librairie front end, tout est exécuté dans le navigateur de l'utilisateur.
Pour créer un projet complet avec une base de données, on aura besoin d'y
ajouter un backend qui pourra être par exemple du PHP, du Node.js, du .NET ou
autre, qui va recevoir les requêtes de votre appli react et écrire ou lire dans la
base de données.
Si vous êtes familiers avec le pattern MVC (modèle - vue - controller), React
est la partie vue.
Librairie ou framework ?
Frameworks concurrents
Il y a 2 autres gros frameworks, qui eux sont réellement des frameworks, qui
permettent de concevoir des applications avec la même philosophie que react,
ce sont Angular et Vue. Angular était extrêmement populaire il y a 5 ou 10 ans,
mais React a vraiment pris le dessus et est aujourd'hui le plus populaire.
Avantages de React
Les composants:
Chaque composant mène sa vie, même s'il est possible d'échanger des infos
entre les composants.
JSX :
React utilise un Virtual DOM, grossomodo une copie "à la sauce react" du DOM
de notre page, qui permet à react de réagir à chaque fois que l'état d'un
composant change et de mettre à jour sur la page uniquement la donnée qui a
changé.
Avec des pages web classiques qui reposent sur un serveur PHP par exemple,
si on supprime un élément à l'écran, on va probablement soumettre une
requête au serveur (par exemple PHP qui va nous répondre avec une nouvelle
page web dans laquelle l'élément a disparu. On a donc un rechargement de la
page, ce qui est très lent pour l'utilisateur.
Pour contourner cela, l'AJAX a vu le jour à l'époque du Web 2.0, qui permet de
mettre à jour dynamiquement des éléments sur la page, mais lorsque la page
devient complexe et que l'on veut que tout soit dynamique, il devient très
compliqué (impossible ? de gérer tout ce code dynamique.
Ici, React surveille en arrière plan quelle donnée a changé et met à jour la page
web dynamiquement sans aucun rechargement, ce qui permet d'avoir des
pages web très performantes et agréables à utiliser pour l'utilisateur. Même
lorsqu'on change d'URL, tout se passe côté frontend de manière immédiate
Objectifs
Comprendre
const/let
arrow functions
promises et async
fetch API
etc...
Afin que ceux qui ne connaissent pas ces syntaxes modernes JavaScript
puissent suivre cette formation React, je vous ai préparé une mini-formation
Objectifs
Comprendre
Le système d'exploitation
Node.JS et npm
L'IDE
Le choix de l'IDE (votre éditeur de code) vous appartient et vous pourrez suivre
la formation avec votre IDE préféré. Si vous n'avez pas d'IDE sur votre poste,
alors je vous recommande fortement l'utilisation du génial Visual Studio Code, à
ne pas confondre avec Visual Studio (tout court), qui, lui, est plutôt adapté au
développement .NET.
Appliquer
Objectifs
C'est parti, nous allons tout de suite créer notre projet React.js !
Comprendre
Il y a plusieurs façons de créer un projet react mais dans 99% des cas on va
utiliser un programme qui s'appelle create-react-app : c'est un programme
en ligne de commande qui permet plusieurs choses :
Il inclut webpack pour bundler tout notre code JavaScript avec celui
des librairies du projet en 1 seul fichier JavaScript que l'on va déployer
en production
Ouvrir un terminal.
Taper code . pour ouvrir le dossier courant dans Visual Studio Code
Objectifs
Comprendre
Lorsque vous allez ouvrir votre projet, vous aurez peut-être des
fichiers ou des dossiers que je n'ai pas, ou inversement.
Si vous avez des fichiers en plus ou en moins, laissez-les tel que vous
les avez, cela ne devrait poser aucun problème.
Si on déplie les dossiers /public et /src , voici à quoi devrait ressembler votre
projet dans VS Code :
trouve aussi des alias de commandes comme npm start , npm build , etc. qui
permettront de facilement démarrer ou builder notre application.
dans packge.json pour dire qu'on installa la dernière version 1.0 disponible,
mais si en l'occurence la dernière version 1.0 disponible est la 1.0.6 , alors
on va tracer dans package-lock.json qu'on a installé la 1.0.6 . Le but est
que tous les développeurs d'une équipe utilisent les mêmes versions.
node_modules : lorsqu'on fait npm install , npm télécharge tous les packages
listés dans package.json depuis le web, et les stocke dans node_modules.
Ce dossier peut donc devenir énorme (parfois des Go sur de gros projets).
Cela permet de comprendre l'utilité du package.json lorsqu'on utilise des
logiciels de gestion de versions comme git : au lieu de commiter les
librairies elles-mêmes (node modules, potentiellement plusieurs Go de
données), on va simplement commiter les fichiers package.json et
package-lock.json qui donnent la liste des librairies à télécharger.
dossiers de notre projet que l'on ne souhaite jamais commiter (comme par
exemple le dossier node_modules )
src : Le dossier le plus important : c'est ici que se trouve le code de notre
application React. Plusieurs fichiers ont été créés pour générer une
application de démo. Expliquer le contenu de ces fichiers reviendrait à
prendre trop d'avance sur la formation, on va donc supprimer l'appli de
démo pour ne garder que le squelette le plus simple possible et partir de
zéro.
Objectifs
Appliquer
Nettoyage du code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React Network</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
Dans le dossier src , supprimer tous les fichiers sauf App.js et index.js .
Une fois qu'il ne vous reste que App.js et index.js , nous allons les simplifier :
Dans App.js :
Tout d'abord, après avoir ouvert App.js , nous allons configurer VS Code
pour qu'il traite le code React comme du code React, et pas comme du
JavaScript standard. Pour cela, cliquez en bas à droite de la barre d'état
bleu sur le langage "JavaScript" et sélectionnez "JavaScript React" dans la
liste.
function App() {
return (
<h1>Hello World</h1>
);
Dans index.js :
Conservez bien import React from 'react' et import ReactDOM from 'react-dom'
Une fois que votre cache est rafraîchi, vous obtiendrez ceci :
Objectifs
Notre objectif dans ce chapitre est de comprendre dans les moindres détails le
peu de lignes de code qu'il nous reste après ce grand nettoyage.
Comprendre
Comment le texte "Hello World" écrit dans App.js se retrouve-t-il affiché dans
le <div id="root"></div> de notre navigateur ?
Ce qu'il faut retenir, c'est que ces scripts font appel à index.js .
Regardons donc ce que fait index.js .
Etape 2 - index.js
ReactDOM.render(
<React.StrictMode>
<App />
document.getElementById('root')
);
de notre HTML.
C'est donc grâce à cette instruction que React va injecter notre composant
App dans le div root.
Dernier point à comprendre : comment React sait-il que <App /> est déclaré
dans App.js ? La réponse est dans la ligne suivante :
Etape 3 - App.js
function App() {
return (
<h1>Hello World</h1>
);
}
export default App;
La ligne export permet d'exporter le module (on parle ici d'une fonctionnalité
javascript d'export de modules, cela n'a rien de spécifique à React). C'est grâce
à cet export que l'on a pu faire import App from './App' dans index.js .
Evaluer
Quizz
Correction
Objectifs
Comprendre
https://babeljs.io > Try it out > Cocher le preset "react" et décocher "Enabled"
dans "Env presets" puis coller notre App.js dans la partie de gauche.
function App() {
const titre = <h1>Bonjour</h1>
return titre
}
function App() {
const name = "Mathieu"
return <h1>Bonjour {name}</h1>
}
Appliquer
Configurer VS Code
Si cela n'a toujours pas été fait, configurer VS Code pour qu'il traite le code
React comme du code React, et pas comme du JavaScript standard. Pour cela,
cliquez en bas à droite de la barre d'état bleu sur le langage "JavaScript" et
sélectionnez "JavaScript React" dans la liste.
Manipulation de JSX
return (
<div>
<p>Un paragraphe</p>
<p>Un autre paragraphe</p>
</div>
);
Attention, avec JSX, on doit obligatoirement avoir une seule balise parente.
return (
<p>Un paragraphe</p>
<p>Un autre paragraphe</p>
);
return (
<React.Fragment>
<p>Un paragraphe</p>
<p>Un autre paragraphe</p>
</React.Fragment>
);
return (
<>
<p>Un paragraphe</p>
<p>Un autre paragraphe</p>
</>
);
Bref, ce qui est entre accolades est réellement exécuté comme du code
JavaScript !
function App() {
const name = "Mathieu"
const elements = (
<div>
<h1>Bonjour {name}</h1>
<div>Cliquez ici pour vous déconnecter :</div>
<button>Se déconnecter</button>
</div>
);
return elements
}
Ne pas afficher le même élément selon si une condition est vérifiée ou non
function App() {
const isConnected = true
return (
<div>
<h2>Bienvenue</h2>
{isConnected ? <button>Se déconnecter</button> : <button>Se connecter</button>}
</div>
)
}
function App() {
const isConnected = true
return (
<div>
<h2>Bienvenue</h2>
{
isConnected
?
<div>
<p>Cliquez ici pour vous déconnecter</p>
<button>Se déconnecter</button>
</div>
:
<div>
<p>Veuillez vous connecter</p>
<button>Se connecter</button>
</div>
}
On pourrait faire :
function App() {
const isConnected = true
return (
<div>
<h2>Bienvenue</h2>
{isConnected ? <button>Se déconnecter</button> : null}
</div>
)
}
function App() {
const isConnected = true
return (
<div>
<h2>Bienvenue</h2>
{isConnected && <button>Se déconnecter</button>}
</div>
)
}
A noter, mais nous le verrons plus tard, que class étant un mot clé réservé en
JavaScript, on ne pourra pas utiliser l'attribut class (permettant d'ajouter du
style CSS sur nos balises HTML, il faut ajouter un attribut className qui sera
converti à l'affichage en class .
Maintenant que nous avons manipulé tous ces concepts qui nous serviront tout
au long de la formation, nous pouvons annuler nos modifications pour rétablir
function App() {
return (
<h1>Hello World</h1>
)
}
export default App;
Evaluer
Quizz
Une facilité de codage qui permet de manipuler des balises HTML et des
composants React comme des variables
On ne peut pas utiliser l'attribut class sur les balises HTML, il faut utiliser
className
Correction
Objectifs
Essayons de comprendre ce que signifie développer une page web sous forme
de composants.
On peut par exemple s'en faire une bonne idée en faisant une rapide analyse
de la page d'accueil de YouTube qui est un exemple parfait d'un
développement en composants (même si YouTube n'utilise pas React).
Ensuite, nous allons enfin créer le tout premier composant de notre application
de réseau social : le composant qui affiche un post !
Comprendre
Cet élément unitaire va pouvoir être customisé en lui passant des données, par
exemple, sur une miniature de vidéo, on va passer au composant chargé
d'afficher la vidéo plusieurs paramètres comme l'image de miniature, le nom de
la chaîne, le nombre de vues, etc... et le composant chargé d'afficher la vidéo
saura exactement où et comment afficher chacune de ces informations.
Du code React est très pratique et agréable à maintenir car il va être découpé
en petites briques unitaires (les composants), comme des Lego, que l'on va
pouvoir piocher et assembler pour former une application entière.
Le code est donc clairement découpé (si les développeurs ont bien fait leur
travail !, facile à maintenir et à explorer.
Appliquer
Pour cela, nous allons avoir besoin de créer un composant Post que l'on va
pouvoir réutiliser pour afficher chacun des posts.
Pour créer notre composant Post, nous allons créer un nouveau fichier, dans
src, que nous allons appeler Post.js .
Pour le moment, notre post va simplement afficher "Bonjour tout le monde !",
codé en dur.
Créer le composant
Comme tout composant, ce composant doit déclarer une fonction qui retourne
le code HTML de notre post.
function Post() {
function Post() {
}
export default Post;
Il ne nous reste plus qu'à retourner "Bonjour tout le monde !" en HTML
function Post() {
return <div>Bonjour tout le monde !</div>
}
export default Post;
Pour cela, nous allons ouvrir App.js puis nous allons faire 2 choses :
Tout d'abord, importer le composant. Pour cela, il faut insérer tout en haut du
fichier :
Puis, nous allons l'insérer là où on souhaite dans notre HTML, par exemple :
function App() {
return (
<>
<h1>Fil d'actualité :</h1>
<Post />
</>
);
}
export default App;
return (
<>
<h1>Fil d'actualité :</h1>
<Post />
<Post />
<Post />
</>
);
function Post() {
return <p>Bonjour tout le monde !</p>
}
export default Post;
💬
function Post() {
return <p> Bonjour tout le monde !</p>
}
export default Post;
Evaluer
Quizz
Correction
Objectifs
Mais pour l'instant, leur contenu est "codé en dur". Notre composant Post , par
exemple, affiche toujours "Bonjour tout le monde".
Comprendre
Les props 1
comme on le souhaite. Par exemple, pour le texte de mon post, je peux appeler
la prop text , cela donnera donc :
Appliquer
function App() {
return (
<>
<h1>Fil d'actualité :</h1>
<Post text="Bonjour tout le monde !" />
<Post text="Comment allez-vous ?" />
<Post text="Vive React.JS" />
</>
);
}
Les props 2
Nous allons donc faire en sorte qu'il affiche la valeur passée en attribut (en
React on parlera de props).
Nous avons maintenant accès à un objet props qui nous permet d'accéder au
texte à afficher en toute simplicité puisqu'il suffit de lire la valeur de props.text
Nous allons donc remplacer Bonjour tout le monde ! par props.text , ou plus
exactement {props.text} puisque rappelez-vous les bases du JSX, il faut des
accolades pour exécuter du code JavaScript au milieu de notre HTML.
💬
function Post(props) {
return <p> {props.text}</p>
}
export default Post;
Les props 3
A retenir
Par exemple, si Youtube était fait en React, sur une liste de vidéos Youtube, un
élément de la liste serait un composant réutilisable avec une structure fixe (la
vignette est à gauche, le titre est en gras, la durée s'affiche en bas à droite de
la vignette, etc.) mais les informations affichées dans cette structure comme
l'image de la vignette, le texte du titre et la durée de la vidéo seraient passées
en props de ce composant.
Les props 4
Evaluer
Quizz
<div>{prenom}</div>
<div>prenom</div>
<div>{props.prenom}</div>
<div>props.prenom</div>
Correction
✅ <div>{props.prenom}</div> Correct
Les props 5
Les props sous toutes les
coutures
Objectifs
Nous avons vu le fonctionnement le plus basique des props : passer une chaîne
de caractères à un composant.
Dans ce chapitre, nous allons manipuler les props sous toutes les coutures
pour savoir les utiliser en toute situation.
Appliquer
Utiliser les propTypes pour typer nos props et/ou les rendre obligatoires
Evaluer
Quizz
Correction
Correct
Objectifs
Dans ce chapitre, nous allons voir comment passer des enfants à notre
composant, toujours avec le mécanisme des props.
Comprendre
Via un attribut
Par exemple, si on souhaite écrire "Bonjour" dans un text input HTML, on va
devoir écrire :
<textarea>Bonjour</textarea>
Côté App.js :
function App() {
return (
<>
<h1>Fil d'actualité :</h1>
<Post text="Bonjour tout le monde !" />
<Post text="Comment allez-vous ?" />
<Post text="Vive React.JS" />
</>
);
}
Côté Post.js :
💬
function Post(props) {
return <p> {props.text}</p>
}
export default Post;
function App() {
return (
<>
<h1>Fil d'actualité :</h1>
<Post>Bonjour tout le monde !</Post>
<Post>Comment allez-vous ?</Post>
A l'écran, le contenu de nos posts a disparu car props.text n'est plus défini.
Nous allons donc faire en sorte qu'il affiche la valeur passée dans le contenu
du tag.
💬
function Post(props) {
return <p> {props.children}</p>
}
export default Post;
Le passage par contenu du noeud est un cas spécifique. Mais quels sont ces
cas spécifiques ?
Il s'agit des composants dont une des props est un arbre de noeuds (un
morceau de HTML par exemple).
Evaluer
Quizz
Correction
bon code ?
❌ return `1: ${props.children}, 2: ${props.message}`
Correct
Objectifs
Premier aperçu des React Dev Tools qui permettent de visualiser l'arbre de nos
composants React et d'interagir avec les props.
Appliquer
La première étape est d'installer les React Dev Tools sur Google Chrome via la
page suivante : https://chrome.google.com/webstore/detail/react-developer-
tools/fmkadmapgofadopljbjfkapdkoienihi
Une fois que cela est fait, nous avons dans notre panneau développeur de
Google Chrome deux nouveaux onglets disponibles : ⚛ Components et ⚛ Profiler .
Cet onglet est très utile puisqu'il permet de visualiser l'arbre de nos
composants :
Objectifs
Nous affichons pour le moment 3 posts en faisant référence aux données "en
dur", avec des indices numérotés.
Nous allons maintenant voir comment boucler sur nos données de manière
générique, et en utilisant les pratiques recommandées en React.
Appliquer
Nous allons boucler sur nos éléments selon plusieurs méthodes, de la plus
naïve (et peu recommandée), à la plus élégante et conforme aux pratiques
recommandées.
La première solution serait de faire une boucle for avant notre return :
let postsComponents = []
for (const p of posts) {
postsComponents.push(<Post postData={p} />)
}
Ceci signifie qu'à chaque fois que nous allons demander à React d'afficher une
liste de composants construite via une boucle dynamique, nous devons donner
un identifiant unique à chacun des composants de la liste.
Cet identifiant devra être passé au composant via les props, dans une prop qui
s'appelle key .
Nous sommes au milieu d'une boucle for avec un incide i qui incrémente à
chaque tour de la boucle. A priori nous avons donc notre identifiant unique,
mais surtout pas ! Il ne faut surtout pas passer l'indice de notre boucle (for ou
map) comme key !
Pourquoi ? Pour cela il faut comprendre comment React utilise la key .
Dans les conditions d'un projet réel, cet identifiant sera très souvent
l'identifiant de l'objet dans la base de données.
Ici, nous allons simuler la présence d'un identifiant type base de données sur
chacun de nos posts, puis nous allons l'utiliser comme key :
const posts = [
{ id: 12, text: "Bonjour tout le monde !", author: "Hugo Bordes", likes: 42 },
{ id: 55, text: "Comment allez-vous ?", author: "Antoine Morin", likes: 35 },
{ id: 90, text: "Vive React.JS", author: "Léa Dumont", likes: 17 },
]
let postsComponents = []
for (const p of posts) {
postsComponents.push(<Post key={p.id} postData={p} />)
}
Plutôt que d'utiliser une boucle classique à effets de bord, nous allons utiliser la
programmation fonctionnelle avec les fonctions fournies par Javascript sur les
tableaux (map, filter, find, reduce, etc...).
let postsComponents = []
for (const p of posts) {
postsComponents.push(<Post key={p.id} postData={p} />)
}
Après :
Avant :
function App() {
const postsComponents = posts.map(p => <Post key={p.id} postData={p} />)
return (
<>
{postsComponents}
</>
);
}
Après :
function App() {
return (
<>
{posts.map(p => <Post key={p.id} postData={p} />)}
</>
);
}
Correction
Objectifs
Dans ce chapitre, nous allons voir comment interagir avec notre application
React.JS via des événements.
Comprendre
Le but d'une application web est d'interagir avec l'utilisateur, par exemple en
faisant une action lorsqu'il clique sur un bouton, lorsqu'il survole un composant,
lorsqu'il scrolle la page, etc...
Pour faire cela dans une application HTML/Javascript classique, on peut définir
un listener sur un élément HTML, par exemple :
document.querySelector('#myButton').addEventListener('click', e => {
console.log("click")
})
Voyons comment faire l'équivalent dans React (c'est encore plus simple !
Appliquer
Créons un composant Header vierge qui va nous permettre de voir clair, sans
être distraits par toutes les props que nous avons traitées jusqu'ici :
function Header() {
Les événements 1
return (
<h1>{title}</h1>
)
<button>Cliquez ici</button>
Créons également une fonction qui correspond au code que nous souhaitons
exécuter lorsque le bouton est cliqué :
Pour déclencher l'exécution de code lorsque ce bouton est cliqué, c'est simple
comme bonjour !
Il suffit de rajouter onClick={onClickHandler} sur notre bouton :
C'est tout !
Les événements 2
function Header() {
return (
<div>
<h1>{title}</h1>
<button onClick={onClickHandler}>Cliquez ici</button>
</div>
)
On peut voir s'afficher "Souris sur le titre" dès qu'on met la souris sur notre h1
et "Souris éloignée" dès qu'elle en ressort.
Note : si vous faites le test et avez l'impression que les événements se
déclenchent à d'autres moments que quand vous mettez la souris sur le titre,
Les événements 3
n'oubliez pas que h1 est un élément de type block qui prend toute la largeur de
la page)
La syntaxe
onClick={maFonction}
Est équivalente à :
Exemple :
function Header() {
return (
<div>
<h1>{title}</h1>
<button onClick={() => sayHello('Vincent')}>Cliquez ici</button>
</div>
)
Les événements 4
export default Header
Grâce à ces connaissances, nous allons pouvoir, dans les prochains chapitres,
par exemple, liker un post lors de l'événement clic sur un bouton.
La variable d'événement
Les événements 5
Le state et le hook useState
Objectifs
Nous ne l'avons pas vu jusqu'ici car notre page est statique, sans la moindre
interaction.
Comprendre
- Sinon :
- Enlever le fond rouge
- Ajouter le fond bleu
- Au démarrage de l’application :
- Initialiser la couleur (bleu ou rouge)
On est obligé de détailler une liste d'instructions que l'ordinateur doit exécuter
pour arriver au résultat souhaité.
A tout instant :
- Si la valeur est ≥ 20, alors le fond est rouge
- Sinon, le fond est bleu
Première différence : pour le fond rouge ou bleu, on ne décrit pas la liste des
instructions, on décrit le résultat souhaité.
Dans l'implémentation, on va le voir juste après, c'est un peu comme si on
générait les 2 résultats possibles (celui avec le fond bleu et celui avec le fond
rouge), et selon la valeur du state, on choisit d'afficher soit l'un soit l'autre.
Le rôle de décideur : c'est lui qui détermine comment sera affiché notre
composant (rouge ou bleu)
Pour faire très simple, le state est la liste de toutes les variables pour lesquelles
React va mettre à jour notre composant à l'écran dès que leur valeur change.
Appliquer
Si vous êtes comme moi et que vous avez besoin de voir les choses par vous-
même pour y croire, allons-y, essayons de modifier le texte de notre Header
lorsque notre bouton est cliqué.
(passez bien la variable title en let et pas const sinon vous ne pourrez même
pas en changer la valeur)
Comme vous pouvez le voir, la variable a beau être modifiée, absolument rien
ne change sur notre écran.
Le hook useState
En React, les hooks sont des fonctions utilitaires. Il existe plusieurs hooks
prédéfinis dans React, et il est également possible de créer ses propre hooks,
nous le verrons plus tard.
Le hook useState est une fonction qui nous permet d'indiquer à React que notre
composant doit en permanence resté synchronisé avec la valeur de cette
variable. Par exemple, dès que notre variable titre devient "Mon nouveau titre",
alors React va synchroniser l'affichage en temps réel.
Pour créer une variable d'état title , nous allons tout d'abord importer le hook
useState :
title est notre variable d'état. Nous pouvons l'utiliser où nous voulons
dans le composant, comme une variable quelconque.
setTitle est une fonction fournie par useState qui permet de mettre à jour
la variable. Si on fait title="Mon nouveau titre" , rien ne se passera. Il faut
toujours faire setTitle("Mon nouveau titre") afin que React soit informé du
changement de valeur de la variable.
function Header() {
return (
<div>
<h1>{title}</h1>
<button onClick={changeTitle}>Cliquez ici</button>
</div>
)
return (
<p>
💬
{props.postData.author}<br />
{props.postData.text}<br />
{nbLikes} likes<br />
<button onClick={likePost}>J'aime</button>
</p>
)
Ajoutons une variable d'état nbLikes qui va être initialisée avec la valeur donnée
en props ( props.postData.likes ) :
<button onClick={likePost}>J'aime</button>
Nous avons bien le nombre de likes qui augmente lorsqu'on clique sur un
bouton !
Pour cela, ajoutons un state isLiked qui va mémoriser si nous avons déjà liké le
post ou pas, avec la valeur par défaut false (au chargement de l'application
nous allons considérer que nous n'avons liké aucun post) :
Modifions la logique de notre fonction : si je n'ai jamais liké ⇒ 1, si j'ai déjà liké
⇒ 1 :
Evaluer
Quizz
Une variable qui cause une actualisation du composant à l'écran dès que sa
valeur change
Rien
Correction
Objectifs
Le state ne se limite pas aux variables simples (string, nombre, booléen, etc).
Nous pouvons également mettre dans le state un tableau d'objets.
Nous allons démontrer ceci en mettant nos posts dans le state afin d'actualiser
l'affichage lorsque nous en supprimons un.
Appliquer
Nous allons ajouter un bouton sur notre page qui va supprimer un post donnée
(par exemple le post avec l'id 90.
Il ne nous reste plus qu'à mettre à jour la variable posts indirectement grâce à
la méthode setPosts :
Nous avons bien un bouton sur la page qui permet de supprimer le post ayant
l'id 90.
A présent, au lieu d'avoir un seul bouton "Supprimer" qui supprime un seul post,
nous souhaiterions que chaque post porte un bouton "Supprimer".
Evaluer
Quizz
Question en gras
Réponse 1
etc
Question en gras
Recopie de la question
Objectifs
Comprendre
A présent, au lieu d'avoir un seul bouton "Supprimer" qui supprime un seul post,
nous souhaiterions que chaque post porte un bouton "Supprimer".
Cela signifie que le bouton "Supprimer" va devoir être placé dans le composant
Post, afin qu'il s'affiche que chaque post
Cela représente une nouveauté : notre bouton qui est dans le composant Post
va devoir modifier le state (la liste des posts) qui se trouve dans un autre
composant (le composant App).
Cela pose de gros problèmes. Si plus tard nous avons besoin de faire une
vérification ou une modification sur la nouvelle valeur du state avant de
l'affecter (par exemple si la nouvelle valeur vaut null, alors je mets zéro dans le
state), nous n'aurons aucun moyen de le faire car le setter sera appelé
n'importe où dans le code, alors que si nous ajoutons des fonction
intermédiaires, qui ont un sens précis (par exemple addPost, deletePost,
likePost, ...) dans notre composant principal et que nous passons ces fonctions
intermédiaires à d'autres composants, alors nous avons toujours la maîtrise de
notre state car toutes les modifications de state d'origine extérieure passeront
par ces fonctions, et nous pourrons donc ajouter n'importe quelle vérification
ou modification sur la valeur avant se faire le setState qui va réellement mettre
à jour la valeur du state.
Nous allons créer une fonction utilitaire deletePost dans le composant App et la
passer au composant Post.
Cette fonction prendra en paramètre l'id du post à supprimer et rien d'autre.
Le Post reçoit en prop une fonction deletePost qu'il peut appeler si l'utilisateur
souhaite le supprimer, mais la maîtrise et la manipulation de l'état reste dans le
composant où est stocké cet état.
Appliquer
Remodeler le code
Notre bouton fait appel à deletePost mais cette fonction est définie dans le
composant App
Evaluer
Quizz
Objectifs
Comprendre
Pour passer des valeurs à des composants enfants, on a vu que l'on utilisait les
props.
Dans les exemples vus jusqu'ici, nous avons passé des valeurs sur un niveau :
le composant qui utilise la valeur est toujours l'enfant immédiat.
Par exemple, les données d'un post particulier sont stockées dans le state du
composant App.
Dans ce cas là, nous allons devoir passer les props de composant en
composant, c'est ce que l'on appelle le props drilling.
Appliquer
App
Post
À l'architecture suivante :
App
Post
PostText
PostDeleteButton
Toutes les informations sur les posts sont stockées dans le state de App.
Certaines informations (l'auteur par exemple) vont devoir descendre jusqu'à
Post.
Le texte du post va devoir descendre jusqu'à PostText.
L'id du post et la référence à la fonction deletePost vont devoir descendre
jusqu'à PostDeleteButton qui a besoin de ces 2 informations pour demander à
App de supprimer le post.
Lorsque les props que l'on passe de niveau en niveau n'ont pas de rapport
avec les composants intermédiaires. Ici, passer une fonction deletePost à un
composant qui gère un flux d'actualité n'est pas choquant, cette fonction
fait réellement partie de ce dont il a besoin pour fonctionner. Par contre, si
je veux gérer une notion de thème de couleur (dark ou clair), si je passe à
mon composant flux d'actualité une props "iconColor" pour gérer la couleur
du pouce j'aime ou de la corbeille supprimer c'est un peu plus gênant car
cela ne fait pas partie du coeur du fonctionnement de ce composant.
Evaluer
Quizz
Question en gras
Réponse 1
etc
Question en gras
Correction
Recopie de la question
Objectifs
Familiarisons nous avec un pattern que l'on rencontre souvent dans les
composants React : l'opérateur ternaire, utilisé par exemple pour des écrans
d'état vide ou des indicateurs de chargement.
Comprendre
Nous avons vu que c'est l'état du composant qui dicte son affichage, comme si
l'affichage était en synchronisation avec les variables du state.
On a utilisé ce principe pour changer un texte à l'écran, avec une logique
binaire : si l'état vaut ceci, j'affiche ce texte, si l'état vaut cela, j'affiche tel texte.
Mais nous pouvons aller plus loin et appliquer une logique du même type qui
porte sur le composant en entier, et pas seulement sur un texte.
Dans notre application, il se peut qu'il n'y ait aucune donnée à afficher sur un
écran. Par exemple, si il n'y a encore aucun post, ou si je supprime tous les
posts. Tel qu'on a développé l'application, si notre variable du state posts est
égale à une liste vide, on aura un écran vide, tout blanc. Une meilleure pratique
serait d'afficher un message "Aucun post pour le moment" afin que l'utilisateur
sache qu'il n'y a aucune donnée, plutôt que de croire que le chargement est
peut-être en cours et que les données vont arriver
Appliquer
Préparation
return (
<>
{/*
<h3>Fil d'actualité :</h3>
{posts.map(p => <Post key={p.id} postData={p} deletePost={deletePost} />)}
*/}
<div align="center">
<h3>Aucun post pour le moment</h3>
</div>
</>
)
Ajoutons maintenant une image pour rendre cet écran plus agréable.
Nous pouvons trouver des illustrations modernes open source sur
https://undraw.co/ en cherchant "empty" comme mot-clé d'image. J'ai choisi
celle-ci :
(nous verrons plus tard comment utiliser l'attribut style pour injecter du CSS
utilisation:
Maintenant que nous avons notre écran d'état vide, il ne nous reste plus qu'à
ajouter la logique qui va afficher soit l'écran vide, soit notre liste de posts, selon
que notre liste de posts contient au moins un post ou pas.
const initialPosts = [
{ id: 12, text: "Bonjour tout le monde !", author: "Hugo Bordes", likes: 42 },
{ id: 55, text: "Comment allez-vous ?", author: "Antoine Morin", likes: 35 },
{ id: 90, text: "Vive React.JS", author: "Léa Dumont", likes: 17 },
]
function Feed(props) {
Cela nous permet de voir qu'avec JXS on peut stocker des composants et des
arborescences HTML dans des variables javascript pour ensuite les insérer
dans notre return .
const initialPosts = [
{ id: 12, text: "Bonjour tout le monde !", author: "Hugo Bordes", likes: 42 },
{ id: 55, text: "Comment allez-vous ?", author: "Antoine Morin", likes: 35 },
{ id: 90, text: "Vive React.JS", author: "Léa Dumont", likes: 17 },
]
function Feed(props) {
Evaluer
Quizz
Question en gras
Réponse 1
etc
Question en gras
Correction
Recopie de la question
Objectifs
Evaluer
Grâce aux notions vues jusqu'ici, nous allons créer un nouveau composant
(pour le moment vide) nommé CreatePost qui va nous permettre d'ajouter un
nouveau post dans notre réseau social.
Ceci va être fait sous forme d'exercice, car nous avons déjà vu les concepts qui
permettent de créer un composant vide, et de l'inclure dans notre application.
Notez bien que pour cet exercice, le but n'est absolument pas de développer le
composant qui va permettre d'ajouter un nouveau post avec une zone de texte,
un bouton, etc... Tout ceci sera fait plus tard.
Consigne n°1 :
Consigne n°2 :
function CreatePost() {
return <div>Nouveau post</div>
}
function App() {
return (
<>
<Header />
<CreatePost />
<Feed />
</>
);
}
export default App;
Résultat :
Objectifs
Nous allons étudier les 2 manières de gérer des champs de formulaire (texte,
cases à cocher, etc) avec React : les inputs contrôlés et les inputs non
contrôlés
Comprendre
Du coup, cette logique peut poser un problème pour les champs de formulaire :
J'aurai probablement dans mon composant une variable d'état prenom qui vaut
"Vincent".
Et mon champ texte utilisera cette valeur, par exemple :
On a donc 2 solutions :
La première (celle qui est recommandée) est de modifier notre state dès que
l'utilisateur veut changer la valeur du champ. Par exemple, lors de la
suppression du "t" de "Vincent", on va modifier notre state pour y stocker la
valeur "Vincen". Vu que l'écran est synchronisé avec le state, on aura bien
"Vincen" à l'écran. On parle d'input contrôlé.
La deuxième (celle qui n'est pas recommandée) est de ne pas du tout utiliser le
state pour gérer ce champ de texte, on va juste passer la valeur initiale (via
l'attribut defaultValue ), et ensuite il pourra évoluer comme il le souhaite car il
n'est pas lié au state. On parle d'input non contrôlé.
Appliquer
function CreatePost() {
return <>
<div>Nouveau post</div>
<input type="text" value={postText}></input>
</>
}
La façon la plus propre de gérer ce cas est tout simplement de modifier notre
state selon ce que l'utilisateur va taper dans le champ.
Pour cela, on utilise l'événement onChange qui est appelé à chaque fois que
l'utilisateur tape ou supprime du texte.
Puisque event.target nous donne accès au text input, et que sur un text input
on peut accéder à la valeur via la propriété value , il nous suffit de faire
event.target.value pour obtenir la valeur que l'utilisateur est en train de saisir.
Nous n'avons plus qu'à mettre à jour le state avec ce que l'utilisateur est en
train de saisir en faisant setPostText(event.target.value) , et le tour est joué :
function CreatePost() {
return <>
<div>Nouveau post</div>
<input type="text" value={postText} onChange={onChangeHandler}></input>
</>
}
Une autre manière de gérer les inputs est possible, mais non recommandée :
laisser l'input vivre sa vie sans aucun lien avec l'état du composant.
Grâce à useRef, nous pouvons créer une référence (toujours passer null à
useRef pour ce use case précis) :
Que nous pouvons ensuite lier à n'importe quel élément HTML, via l'attribut ref,
comme ceci :
function CreatePost() {
return <>
<div>Nouveau post</div>
<input type="text" ref={inputRef}></input>
<input type="submit" value="Publier" onClick={handleSubmit} />
</>
}
Par exemple, si on veut créer un bouton qui permet de donner le focus à notre
zone de texte, on n'aura pas d'autre choix que de passer par une référence
pour appeler la fonction .focus() native du DOM :
function CreatePost() {
return <>
<div>Nouveau post</div>
<input type="text" ref={inputRef}></input>
<button onClick={handleClick}>Donner le focus au champ texte</button>
</>
}
A noter que le mécanisme d'input contrôlé ou non contrôlé s'applique à tous les
types d'inputs (zones de texte, case à cocher, liste déroulante, etc...). Par
exemple, pour avoir une case à cocher contrôlée par le state, on va utiliser un
state de type booléen.
function CreatePost() {
return <>
<div>Nouveau post</div>
<input type="text" value={postText} onChange={onChangeHandler}></input>
<button onClick={createPostHandler}>Publier</button>
</>
}
Pour que ce post s'affiche à l'écran, il suffit de l'ajouter au state posts ( qui se
trouve dans Feed ).
Dans Feed, nous allons donc ajouter la fonction qui permet d'ajouter un
nouveau post dans le state :
Evaluer
Quizz
Objectifs
Nous allons voir comment faire lorsque le composant qui contient une donnée
et un composant qui doit y accéder (via props) ne sont pas dans la même
branche de l'arbre.
Comprendre
Si B détient un state que C doit pouvoir modifier via une fonction utilitaire, cela
ne sera pas possible.
La solution est de remonter le state dont B et C doivent partager l'utilisation au
niveau de l'ancêtre commun le plus proche.
Ici, l'ancêtre commun est le composant A.
Le state lifting, c'est donc faire remonter l'état à l'ancêtre commun le plus
proche de tous les composants qui ont besoin d'y accéder.
Appliquer
Le state va donc remonter dans App, qui est l'ancêtre commun de ces 2
composants.
On va donc avoir :
Réorganisation du code
Le state posts (car le but de l'opération est de le faire remonter dans App)
Il va donc falloir passer posts et deletePost en tant que props depuis App vers
Feed .
Et enfin, on peut passer addPost de App vers CreatePost, ce qui était le but de
toute cette réorganisation.
On obtient donc :
Feed.js :
App.js :
function App() {
return (
<>
<Header />
<CreatePost addPost={addPost} />
<Feed posts={posts} deletePost={deletePost} />
</>
);
}
export default App;
Si tout s'est bien passé, il ne nous reste qu'une chose à faire : appeler la
fonction addPost depuis le composant CreatePost et le nouveau post apparaîtra
dans le flux d'actualité.
J'en profite pour rappeler que faire function CreatePost({ addPost }) et utiliser
addPost est strictement équivalent à faire function CreatePost(props) et utiliser
props.addPost .
Evaluer
Quizz
Question en gras
Réponse 1
etc
Question en gras
Correction
Recopie de la question
Objectifs
Appliquer
Evaluer
Après avoir cliqué sur "Publier", il est étrange de voir le texte de notre post
rester dans le champ de texte.
Dans la plupart des applications que l'on utilise, lorsqu'on a soumis une
information, le contenu du champ est effacé.
À vous de l'implémenter !
Correction
Il suffit de réinitialiser le state postText à une valeur vide juste après avoir créé
le post :
Objectifs
Comprendre
compressé.
Le pattern && 1
posts.length > 0 ? contentPosts : contentEmpty
On va plutôt faire :
Appliquer
Nous allons voir ensemble comment le faire avec un if classique, puis avec
l'opérateur &&.
Le pattern && 2
let publishButton = null
if (postText != '') {
publishButton = <button onClick={createPostHandler}>Publier</button>
}
return <>
<div>Nouveau post</div>
<input type="text" value={postText} onChange={onChangeHandler}></input>
{publishButton}
</>
}
La syntaxe qui précède est verbeuse. Dans la plupart des projets, vous ne
verrez pas ceci mais plutôt une syntaxe avec des &&.
return <>
<div>Nouveau post</div>
<input type="text" value={postText} onChange={onChangeHandler}></input>
{postText != '' && <button onClick={createPostHandler}>Publier</button>}
</>
}
Si postText est vide, notre bloc va renvoyer false , et false ne va générer aucun
composant à l'écran, donc le bloc ne rendra aucun élement.
Par contre, si postText n'est pas vide, notre bloc va renvoyer le <button> qui va
donc s'afficher à l'écran.
Le pattern && 3
Posts avec images, photos de
profils et heure du post
Objectifs
Le prochain chapitre sera consacré à la mise en place des styles CSS de notre
application.
Avant de nous pencher sur les styles, ajoutons à notre applications les
éléments qui manquent comparé au résultat final souhaité :
Appliquer
Nous allons nous appuyer sur 2 ressources externes dans cette partie.
Afin d'afficher des images sans avoir à gérer l'upload (sujet qui nous polluerait
car il s'agit d'un sujet back-end n'ayant rien à voir avec React), nous allons
utiliser https://picsum.photos. Grâce à ce site, nous allons pouvoir passer un
identifiant aléatoire (par exemple "postpicture-12") dans une url et obtenir une
image aléatoire. Si on réutilise le même identifiant, on obtient la même image,
ce qui permet d'avoir une persistance à l'affichage.
Pour convertir une date en un format texte, nous allons utiliser la librairie
moment.js.
Nous reviendrons plus tard sur la gestion des librairies avec React, pour ce cas
précis sachez simplement que vous devez lancer la commande suivante : npm
Objectifs
Nous allons enfin rendre notre application un peu plus jolie en y ajoutant des
styles CSS.
Comprendre
2 particularités de syntaxe :
Puisque nous écrivons nos composants en JSX, nous sommes dans du code
JavaScript.
En JavaScript, le mot-clé class est réservé pour créer une classe, nous allons
devoir utiliser className à chaque fois que nous voulons ajouter une classe sur
un composant.
Le camelCase
Comme pour le point précédent, nous écrivons nos composants en JSX donc
nous sommes dans du code JavaScript.
Par exemple, dans du code JavaScript (sans React), si vous voulez changer la
couleur de fond d'un titre, vous allez faire :
document.querySelector('h1').style.backgroundColor='blue'
Vous allez donc utiliser backgroundColor qui est la propriété javascript donnant
accès à la propriété CSS background-color .
Avec React, puisqu'on est en JavaScript, on va donc utiliser cette syntaxe que
l'on appelle camelCase.
Style inline
Modules CSS
styled-components
Appliquer
Cette partie est complètement optionnelle car ce n'est pas du React mais du
pur CSS.
Si vous souhaitez rester concentré sur React et sauter cette vidéo, vous
pouvez télécharger les sources du projet (incluant le CSS telles que je les ai
sauvegardées à la fin de cette vidéo via le lien suivant :
https://vincentandrieu.fr/react-network-after-css.tar
Vous pouvez donc récupérer le CSS via le lien ci-dessus et continuer sur React
en passant directement au chapitre suivant.
Objectifs
Dans ce chapitre, nous allons apprendre à utiliser le hook useEffect qui va permettre à
nos composants de déclencher des effets de bords, nécessaires à l'exécution de
morceaux de code ne répondant pas aux lois de la programmation déclarative.
Comprendre
Tout d'abord, sans utiliser useEffect, notons que l'on peut exécuter du code à chaque
rendu de notre composant, de la manière suivante :
function App() {
Rappelons que React déclenche un rendu de notre composant lorsque son state
change.
Le hook useEffect 1
En résumé, cela signifie que si prenom, nom ou ville change, notre code
console.log("Bonjour " + nom) va être exécuté.
function App() {
useEffect(() => {
console.log("Bonjour " + nom)
})
La différence est subtile : le code n'est plus exécuté juste avant chaque rendu, mais
juste après chaque rendu.
Dans le cas où nous utilisons des références (via le hook useRef vu lors du chapitre sur
les champs non contrôlés), nous aurons accès à ces références car React a eu le temps
de rendre le composant (exécuter le "return") alors que dans le cas précédent, on
exécute notre code avant le return, donc avant le rendu, donc nos références n'ont pas
été positionnées par React.
function App() {
useEffect(() => {
console.log("Bonjour " + nom)
}, [nom])
Le hook useEffect 2
On peut avoir plusieurs dépendances à notre hook useEffect, par exemple ici à la fois
nom et prenom : lorsque n'importe laquelle de ces 2 valeurs change, on va ré-exécuter
notre fonction :
function App() {
useEffect(() => {
console.log("Bonjour " + prenom + " " + nom)
}, [prenom, nom])
Le cas d'utilisation le plus fréquent est celui où on veut charger des données (depuis un
backend) au chargement de notre composant.
Pour faire cela, on va passer une liste de dépendances vide :
function App() {
useEffect(() => {
console.log("Bonjour " + nom)
}, [])
Lorsque notre hook met en place des mécanismes de souscription (exemple: des event
listeners), il est possible de se désabonner de ces souscriptions au moment d'un
éventuel démontage du composant en renvoyant une fonction en return de notre hook.
C'est la fonction de "cleanup" :
function App() {
useEffect(() => {
console.log("Bonjour " + nom)
Le hook useEffect 3
return () => console.log("Au revoir " + nom);
}, [])
Appliquer
Une application concrète de notre hook useEffect va être de charger les données depuis
un backend.
Comme nous n'avons pas de backend, nous allons simuler un backend en créant une
fonction de type Promise qui va attendre 2 secondes avant de renvoyer les posts :
La valeur initiale de notre state posts ne sera plus la constante initialPosts mais un
tableau vide :
Grâce à notre hook useEffect, nous allons pouvoir appeler la fonction bouchonBackend pour
récupérer nos posts au démarrage de notre application et les injecter dans notre state :
useEffect(() => {
bouchonBackend().then(posts => {
setPosts(posts)
})
}, [])
Le hook useEffect 4
import { useEffect } from 'react'
Problème : lorsque les données sont en train de charger (pendant les 2 secondes), on a
notre empty state qui dit qu'il n'y a aucun post. Or, c'est faux, ce n'est pas qu'il n'y a pas
de posts, c'est qu'on est en train de les charger.
Ce use case est très commun dans n'importe quelle application.
On va donc créer un state isLoading : s'il est true , on affiche "Chargement..." et s'il est
false , on affiche les posts (ou le empty state s'il n'y a aucun post) :
Dans App.js :
useEffect(() => {
bouchonBackend().then(posts => {
setPosts(posts)
setIsLoading(false)
})
})
Dans Feed.js :
Evaluer
Le hook useEffect 5
Quizz
Correction
Le hook useEffect 6
Les composants basés sur des classes
Comprendre
Avant l'arrivée des hooks dans react (v16.8, il existait 2 types de composants : les composants fonctionnels (ceux
qu'on a utilisé tout le long de la formation) et les composants classes.
Mais il n'était pas possible dans un composant fonctionnel d'utiliser le state. Pour avoir un state il fallait utiliser un
class-based component. Quasiment tous les composants étaient donc des classes.
Tout ceci a changé avec React 16.8 et l'arrivée des hooks (useState, useEffect,...) qui permettent de faire dans un
composant fonctionnel tout ce qu'on faisait dans un composant classe, et de le faire mieux et plus facilement.
Selon beaucoup d'experts, il est recommandé à ceux qui débutent react de ne carrément pas s'y intéresser du tout. En
effet :
Le code est plus difficile à écrire, il y a des pièges dans lesquels on peut facilement tomber
Tous les state d'un composant sont regroupés dans un même state parent qui doit être initialisé dans un
constructeur
les lifecycle methods (qui permettaient de faire ce qu'on fait aujourd'hui avec useEffect) étaient source de
nombreux bugs notamment en cas d'update des props
Il faut faire un .bind(this) sur les handlers pour faire suivre le scope du composant
Je partage cette analyse, mais dans les jobs que vous allez trouver sur le marché, vous allez tomber sur des projets
initialisés avant le printemps 2019 (la fameuse v16.8 qui sont truffés de composants basés sur des classes. Je pense
donc qu'il faut vous expliquer leur fonctionnement afin que vous sachiez travailler sur de tels projets.
Appliquer
Quelles sont les différences concrètes entre un composant fonctionnel et un composant sous forme de classe ?
function onPostTextChangeHandler(event) {
setPostText(event.target.value)
}
const onPostPictureChangeHandler = (event) => {
setPostPicture(event.target.value)
}
useEffect(() => {
console.log("Chargement du composant fonctionnel");
return () => console.log("Démontage du composant fonctionnel");
}, [])
return (
<div className="post">
<div className="createpost-row">
<div>Composant fonctionnel</div>
</div>
<div className="createpost-row">
<div>Texte :</div>
<input type="text" onChange={onPostTextChangeHandler} value={postText} placeholder="Quoi de neuf aujourd'hui ?" />
</div>
<div className="createpost-row">
<div>Photo :</div>
<input type="text" onChange={onPostPictureChangeHandler} value={postPicture} placeholder="URL de la photo" />
</div>
{postHasText && (
<div className="createpost-row">
<button className="btn" onClick={createPostHandler}>Publier</button>
</div>
)}
</div>
)
}
Voici le résultat :
constructor(props) {
super(props)
this.state = {
postText: '',
postPicture: '',
}
}
componentDidMount() {
console.log("Chargement du composant classe");
}
componentWillUnmount() {
console.log("Démontage du composant classe");
}
render() {
return (
<div className="post" >
<div className="createpost-row">
<div>Composant classe</div>
</div>
<div className="createpost-row">
<div>Texte :</div>
<input type="text" onChange={this.onPostTextChangeHandler} value={this.state.postText} placeholder="Quoi de neuf aujourd'hui
</div>
<div className="createpost-row">
<div>Photo :</div>
<input type="text" onChange={this.onPostPictureChangeHandler} value={this.state.postPicture} placeholder="URL de la photo" /
</div>
{ postHasText && (
<div className="createpost-row">
<button className="btn" onClick={this.createPostHandler}>Publier</button>
</div>
)}
</div>
)
On remarque qu'il est plus compliqué à lire (beaucoup de this. qui polluent la lecture), plus verbeux, et qu'il est plus
facile de créer des effets de bords sans s'en rendre compte.
Objectifs
Comprendre
Les hooks : une fonctionnalité ajoutée depuis react 16.8. Ils nous aident à gérer
nos composants. Avant react 16.8, cela n'existait pas et on était obligé de tout
faire de manière plus complexe avec des components basés sur des classes.
La doc react catégorise les hooks en 2 catégories, ceux que l'on va utiliser
dans 95% des cas : les hooks "de base", et les autres, les "hooks
supplémentaires"
useState
useEffect
useContext
Hooks supplémentaires
useReducer
useCallback
Les hooks 1
useMemo
useRef
useImperativeHandle
useLayoutEffect
useDebugValue
Nous n'allons pas tous les voir en détail car certains d'entre eux sont
extrêmement peu utiles et les expliquer créerait plus de confusion qu'autre
chose.
Mais il est également possible de créer nos propres hooks, ce qui va être très
utile pour créer des fonctionnalités réutilisables au travers de nos composants.
Les hooks 2
Custom hooks
Objectifs
Créons 3 custom hooks pour étudier l'étendue des possibilités offertes par ce
mécanisme.
Comprendre
Un hook est juste une fonction réutilisable, une fonction utilitaire. Ceux qui sont
fournis de base dans react (useState, useEffect) proposent des fonctions
avancées, mais en réalité un hook est simplement une fonction.
N’importe quel morceau de code qui a vocation à être réutilisée dans plusieurs
composants peut être un hook.
Ceci dit, un custom hook qui n’utilise pas de hooks (par exemple une fonction qui
réalise un calcul aussi complexe soit-il) pourrait être une simple fonction utilitaire
créée dans un fichier utils.js que l'on importerait dans notre composant, un hook
ne serait pas nécessaire pour cela.
La vraie puissance des hooks se situe dans la possibilité d'utiliser d'autres hooks
(comme useState ou useEffect) dans notre custom hook, pour créer des choses
assez complexes.
Custom hooks 1
Récupérer des données via une API et renvoie non seulement les données
récupérées, mais aussi un indicateur de chargement
Appliquer
Un hook qui n'est pas vraiment un hook (cela en est un, mais cela pourrait être
une simple fonction utilitaire) serait un hook qui prend des données en entrée
pour sortir un résultat, sans utiliser de hooks, par exemple :
function useDouble(nb) {
return nb * 2;
}
export default useDouble;
Un hook un peu plus intéressant serait un hook qui loggue dans la console toute
modification d'une variable.
On pourrait le coder ainsi :
useEffect(() => {
console.log("Nouvelle valeur :", variable);
}, [variable])
}
export default useLogVariable
Et on l'utiliserait ainsi :
useLogVariable(postText)
Custom hooks 2
import { useState, useEffect } from 'react'
useEffect(() => {
setValueInStorage(storageKey, data)
}, [data])
}
export default useLocalStorage
On peut ainsi persister nos posts en storage afin que tous les posts créés soient
conservés lorsqu'on rafraichit notre page web :
Custom hooks 3
Ajouter d'autres pages avec
react router
Objectifs
Créer une application page ayant plusieurs pages avec react router.
Comprendre
Sur un site web classique, lorsque l'on navigue entre les pages, on charge un
nouveau fichier HTML depuis le serveur.
A chaque clic sur notre menu de navigation, nous avons donc un chargement
d'un fichier HTML (ainsi que tous les fichiers CSS et JS associés) depuis le
serveur.
Appliquer
Pour créer Homepage, nous allons prendre tout ce que nous avons
actuellement dans App.
Pour About et Profile, nous allons simplement afficher le texte "about" et
"profile" sur la page.
Pour l'instant, ajoutons nos 3 pages dans App, les unes à la suite des autres :
function App() {
return (
<div>
<HomePage />
<AboutPage />
<ProfilePage />
</div>
)
}
Installation
Mise en place
function App() {
return (
<div>
<Route path="/">
<HomePage />
</Route>
<Route path="/about">
<AboutPage />
</Route>
<Route path="/profile">
<ProfilePage />
</Route>
</div>
)
}
export default App;
Nous pouvons voir que sur la Homepage tout est ok, about et profile ont
disparu, mais sur about et profile, nous avons toujours l'ensemble des pages.
La raison est que :
Toutes les routes sont évaluées, donc même si on matche avec succès la
première route ( <Route path="/"> ), on va quand même évaluer les routes
suivantes
<Switch>
<Route path="/">
<HomePage />
</Route>
<Route path="/about">
<AboutPage />
</Route>
<Route path="/profile">
<ProfilePage />
</Route>
</Switch>
Ceci est dû au match permissif dont on a parlé plus haut. /about matche /
Grâce à l'attribut exact , nous allons pouvoir passer d'un match permissif à un
match exact sur le path / , comme ceci :
<Switch>
<Route exact path="/">
<HomePage />
</Route>
...
</Switch>
<Route path="*">
<h3>Erreur 404 - Not found</h3>
</Route>
<Route path="*">
<Redirect to="/" />
</Route>
Navigation
Pour naviguer entre les différentes pages, on peut ajouter des liens ( <a
href="..."></a> ) dans notre barre de Header, qui pointent sur / , /about et
/profile .
Lorsque l'on clique dessus, la navigation fonctionne bien, mais on voit que le
navigateur fait appel à une nouvelle page et la page s'actualise avec le nouveau
contenu.
function Header(props) {
return (
<div className="header">
<Link to="/" className="header_left">
<h1>React Network</h1>
</Link>
<nav>
<ul>
<li>
<Link to="/about">À propos</Link>
</li>
<li>
<Link to="/profile">Mon profil</Link>
</li>
</ul>
</nav>
</div>
)
}
Objectifs
Appliquer
Afin de récupérer des paramètres depuis notre URL (par exemple pour l'URL
/profile/12, on veut récupérer 12, on fait ainsi :
Tout d'abord, déclarer le paramètre et lui donner un nom dans notre route, ici
nous l'appellerons id :
<Route path="/profile/:id">
<ProfilePage />
</Route>
Enfin, nous allons utiliser le hook useParams fourni par react-router pour
récupérer la valeur de id :
function ProfilePage() {
return (
<div>
<h1>Page Mon profil</h1>
<h3>Ceci est le profil numéro {useParams().id}</h3>
</div>
On peut avoir autant de paramètres que l'on souhaite, par exemple avec la
route <Route path="/profile/:id/item/:itemid"> j'aurais un paramètre id et un
paramètre itemid .
Objectifs
La mise en place d'une mise en page (layout) commune à nos différentes pages
va nous permettre d'avoir une apparence uniforme sur toutes les pages de notre
application.
Appliquer
En créant une mise en page (layout) commune à nos 3 pages, nous allons avoir
le header et la barre de navigation sur nos 3 pages, pas seulement la page
d'accueil.
Pour cela, nous allons découper le code de notre HomePage, qui est le suivant :
<Header />
<div className="center500px">
<CreatePost addPost={addPost} />
<Feed posts={posts} deletePost={deletePost} likePost={likePost} isLoading={isLoading} />
</div>
En 2 parties distinctes :
Tout d'abord, les parties qui ont vocation à être communes à toutes nos pages
vont être déplacées dans un nouveau composant que l'on va appeler
MainLayout.js . Il s'agit de <Header /> et de <div className="center500px"> :
</div>
</>
)
}
<MainLayout>
<CreatePost addPost={addPost} />
<Feed posts={posts} deletePost={deletePost} likePost={likePost} isLoading={isLoading} />
</MainLayout>
function MainLayout(props) {
return (
<>
<Header />
<div className="center500px">
{props.children}
Pour cela, il nous suffit d'aller dans App.js et de mettre notre layout autour du
switch :
function App() {
return (
<div>
<MainLayout>
<Switch>
<Route exact path="/">
<HomePage />
</Route>
<Route path="/about">
<AboutPage />
</Route>
<Route path="/profile">
<ProfilePage />
</Route>
<Route path="*">
<Redirect to="/" />
</Route>
</Switch>
</MainLayout>
</div>
)
}
export default App;
Objectifs
Jusqu'ici, pour accéder à un state depuis un autre composant, nous avons vu qu'il faut élever le state à
l'ascendant commun le plus proche (state lifting) et passer les props sur tous les niveaux intermédiaires.
Voyons comment nous pouvons éviter cela avec la gestion d'un state global à l'application.
Comprendre
La grande quantité de code et de configuration qu'il faut écrire pour mettre en place redux sur un projet
avant même de pouvoir l'utiliser (ce qu'on appelle le boilerplate)
L'introduction de nouveaux concepts Actions, reducers, store, dispatch) qui sont accessibles à tous et
sont utiles pour gérer d'énormes states sur d'énormes applications, mais rajoutent tout de même une
certaine complexité à notre application.
Une autre librairie notable est easy-peasy, qui est une surcouche de redux et qui gère pour nous tout
l'aspect boilerplate/configuration afin que l'on puisse avoir une sorte de "redux plug and play".
C'est de loin la solution que je préfère. React nous fournit depuis la v16.3 une manière simple et efficace de
gérer un état global, sans ajouter aucun outil externe ni aucun concept comme les actions, reducers, store
et dispatch de redux.
Appliquer
UserContext.js:
Nous allons ensuite envelopper notre application entière dans le "Provider" de ce contexte. Le Provider est
un composant fourni par la fonction createContext que nous avons appelée :
App.js:
function App() {
return (
<UserContext.Provider>
<MainLayout>
<Switch>
<Route exact path="/">
<HomePage />
</Route>
...
</Switch>
</MainLayout>
</UserContext.Provider>
)
}
Puisque nous souhaitons avoir un state global qui gère un user, nous allons le créer avec useState et passer
le user au provider via l'attribut value :
function App() {
return (
<UserContext.Provider value={{ user }}>
...
Ça y est, notre state user est partagé avec tous nos composants !
function AboutPage() {
const { user } = useContext(UserContext)
return <p>Vous êtes connecté en tant que {user.author}.</p>
}
Dans notre page "Mon profil", le but est de pouvoir modifier le user stocké dans le state global. On va donc
ajouter setUser à notre contexte :
function ProfilePage() {
Et notre user est bien mis à jour dans notre state global !
Objectifs
Appliquer
useMemo
Dans le corps de notre composant, nous allons faire appel à une fonction calcul
qui fait une opération avec le count (par exemple l'élever au carré) et qui affiche
la valeur dans le composant.
<div>
<h5>Compteur</h5>
<button onClick={() => setCount(count - 1)}>-</button>
{count}
<button onClick={() => setCount(count + 1)}>+</button>
<span>Le résultat est {result}</span>
</div>
<div>
<h5>Switch</h5>
{isOn ? "ON" : "OFF"}
<button onClick={() => setIsOn(!isOn)}>Switch</button>
</div>
Mais imaginons maintenant que le calcul soit une opération synchrone complexe
:
Pour cela, entourons notre appel à la fonction avec le hook useMemo et ajoutons
le count comme dépendance (ce qui signifie que tant que le count ne change
pas, on ne refait pas le calcul et on garde la dernière valeur) :
useCallback
useEffect(() => {
const res = props.calcul(props.count)
console.log("resultat=" + res)
setResult(res)
}, [props.count, props.calcul])
return (
<div style={{ border: '1px solid' }}>
<h5>ShowResult</h5>
Le résultat est {result}
</div>
)
Notons que les 2 hooks ont une signature similaire 1er argument : fonction,
2eme argument : liste des dépendances) et des fonctionnalités quasi similaires :
En effet, useMemo mémorise la valeur de retour de la fonction que l'on lui passe. Si
on lui passe () ⇒ myFunction , il va donc mémoriser la fonction elle-même, c'est-à-
dire myFunction . Ce sera le même objet, donc la même référence, c'est donc
exactement ce que fait useCallback(myFunction) .
Objectifs
Dans une application d'entreprise, notre frontend React va devoir récupérer des
données depuis une API.
Voyons comment faire cela en appelant réellement une URL distante pour récupérer
des données.
Appliquer
Jusqu'ici, nos posts étaient récupérés soit depuis une variable locale "en dur", soit
depuis le local storage.
Nous allons maintenant appeler une URL distante pour récupérer nos données.
Pour créer un backend qui nous envoie les données des posts, le plus simple est
d'utiliser un service spécialisé dans les mockups d'API.
Dans mocky, nous allons créer une URL qui renvoie le JSON correspondant à nos
posts.
Tout d'abord, nous allons avoir besoin d'installer node-fetch pour utiliser le Fetch
API qui permet de faire des requêtes HTTP :
useEffect(async () => {
const response = await fetch('https://run.mocky.io/v3/eb97ebc6-f2a5-4c49-8504-a5752f9292cf')
const postsData = await response.json()
setPosts(postsData)
setIsLoading(false)
}, [])
Nous voyons bien le texte "Chargement..." pendant le petit temps de requête puis
nos posts apparaissent à l'écran !
Objectifs
Savoir tirer profit de l'immense base de packages open source npm pour
enrichir nos projets.
Appliquer
On a déjà utilisé npm install pour installer diverses librairies comme react-
router ou node-fetch, mais là on va voir de manière plus concrète comment
utiliser un package npm dédié à l'interface graphique, pour intégrer des
composants complexes à notre application en un temps record par rapport au
cas où nous devrions les implémenter.
Objectifs
Nous allons enfin déployer notre application en live, elle sera disponible sur le
web via une URL que nous pouvons partager avec n'importe qui !
Appliquer
Rappelons qu'une application React est une application 100% front-end (elle
peut appeler des API backend mais ce backend est déployé ailleurs, sur un
serveur), et donc elle ne fait appel à aucune technologie serveur PHP,
Node.JS, Ruby, etc...). C'est uniquement du HTML, du Javascript et du CSS.
Cela facilite les choses pour le déploiement car nous pouvons utiliser n'importe
quel accès FTP à un serveur pour déposer nos fichiers et le site sera en ligne.
Si vous n'avez pas de serveur pour déposer vos fichiers, nous allons utiliser
netlify, un service en ligne gratuit qui vous permettra d'uploader votre
répertoire et qu'il soit instantanément déployé en ligne !
Un nouveau répertoire "build" a été créé dans votre projet : il contient le site
web optimisé et packagé.
Ouvrez votre compte netlify, dans l'onglet 'Sites', faites glisser votre dossier
build dans la zone de drag and drop, et votre site est en ligne !
Evaluer
Quizz
Des fichiers HTML, CSS et JavaScript, mais aussi du Node.JS pour faire
tourner la partie serveur
Correction
Quid de la suite ?
Une fois que cela sera fait, vous pourrez éventuellement aller encore plus loin
avec React :
Il est possible d'ajouter des surcouches à React.JS afin de palier à son principal
problème qui est le SEO, dû au fait que le rendu est fait côté client Client Side
Rendering) :
Conclusion 1