Vous êtes sur la page 1sur 183

Présentation et introduction

Bienvenue dans cette formation React.JS !

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 !

Dans cette formation, on va couvrir React de A à Z, en apprenant tous les


concepts de React et tous les cas d'utilisation concrets que vous verrez
fréquemment dans des projets React, afin que vous soyez armés pour travailler
sur des projets React concrets juste après la formation.

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.

Avant de démarrer la formation, quelques précisions sur le contenu...

Le projet fil rouge


Nous allons travailler sur un projet qui sera le fil rouge de la formation. En effet,
apprendre la théorie sans jamais l'appliquer ne fonctionnera pas. Nous allons
donc utiliser chaque concept que nous allons apprendre pour créer petit à petit
notre projet.
Il s'agit d'un squelette de réseau social type Facebook.

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 :

1. Nous passerions beaucoup de temps à développer des choses qui n'ont


pas de rapport avec react. Par exemple, si j'implémente un upload d'image,
je vais devoir ajouter du code javascript qui n'a rien à voir avec react et du
code backend pour stocker l'image. De même, si je veux que l'interface soit
très esthétique, je vais passer beaucoup de temps sur le CSS. Et cela a peu
de chances de vous intéresser car soit vous connaissez déjà ces choses là,
auquel cas cela va vous faire perdre votre temps, soit vous ne les
connaissez pas et le peu que l'on verra ici ne serait pas suffisant pour vous
former (il faudrait une formation complète backend ou css par exemple).

2. Nous développerions des dizaines et des dizaines de composants, nous


verrions et reverrions donc chaque concept des dizaines de fois, ce qui
rendrait la formation plus difficile à suivre (on n'a pas une leçon qui sert de
référence pour un concept) et très répétitive. Nous allons donc simplement
développer les parties nécessaires pour avoir un projet utilisable, qui tient la
route, et qui nous permette d'étudier toutes les notions React possibles,
mais nous n'irons pas au bout du développement car cela serait très
chronophage, tout en n'apportant aucun concept supplémentaire.

100% frontend, pas de backend


Notre but est d'apprendre React. Dans la vraie vie, les applications front-end
dialoguent avec des API pour lire et écrire des données. Nous verrons à la toute
fin de la formation comment requêter des données depuis un backend, mais
pendant la quasi totalité du développement de notre projet, nous utiliserons
des données codées en dur afin de ne pas perdre de temps à mettre en place
un back-end qui serait complètement hors sujet.

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 ce qu'est réellement la librairie React.JS, et pourquoi l'utiliser.

Comprendre

Une librairie front-end

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.

Ici on ne va traiter que de react, et on va simuler le backend pour comprendre


comment on utilise react en conditions réelles.

Si vous êtes familiers avec le pattern MVC (modèle - vue - controller), React
est la partie vue.

Librairie ou framework ?

Qu'est-ce que React.js et pourquoi l'utiliser ? 1


Beaucoup en parlent en tant que framework car habituellement une librairie est
un utilitaire duquel on extrait quelques fonctions qui nous aident dans nos
algorithmes, et en général une librairie est une sorte de fonctionnalité
supplémentaire par dessus notre application, alors qu'avec react, on a plutôt
l'impression que c'est notre application qui repose sur react, comme c'est le
cas avec un framework, mais techniquement c'est une librairie que l'on peut
inclure avec un simple tag <script> depuis le header d'un fichier html.

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

Toute notre interface va être découpée en composants unitaires réutilisables


qui portent leur propre état en eux et qui réagissent dynamiquement aux
changements d'état.

Les composants:

Similaire à ce que l'on peut faire avec Figma par exemple.

Permet de structurer simplement et clairement notre front end. On peut coder


des applications énormes tout en conservant une architecture claire car on
raisonne en composants unitaires qui n'ont pas ou peu d'effets de bords entre
eux.

Chaque composant mène sa vie, même s'il est possible d'échanger des infos
entre les composants.

Qu'est-ce que React.js et pourquoi l'utiliser ? 2


Une énorme application javascript sans react serait beaucoup plus difficile à
maintenir et débugger car il n'y a pas de limite claire entre les différents
morceaux de code.

JSX :

La syntaxe JSX permet d'écrire du HTML dynamique directement dans le code


javascript du composant, ce qui est rend le développement extrêmement facile
et agréable.

State et Virtual DOM :

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

Qu'est-ce que React.js et pourquoi l'utiliser ? 3


Programmation déclarative :

L'approche déclarative (contrairement à l'approche impérative) permet d'éviter


les effets de bord, nous reviendrons en détail sur ce point dans un module
dédié.

Qu'est-ce que React.js et pourquoi l'utiliser ? 4


Pré-requis : Le "JavaScript
moderne"

Objectifs

La syntaxe JavaScript moderne est un pré-requis impératif avant d'aller plus


loin dans la formation.

Comprendre

Cette formation React a pour pré-requis de connaître la syntaxe moderne de


JavaScript (versions ES6 et plus).

Voici des exemples de fonctionnalités apparues après ES6 :

const/let

arrow functions

deconstruct / spread / rest

map, filter, etc... (high order array methods)

modules avec import et export

backticks et template strings

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

Pré-requis : Le "JavaScript moderne" 1


gratuite pour apprendre tous les concepts qui seront nécessaires pour suivre la
formation React.

Il est absolument nécessaire de connaître ces concepts pour pouvoir suivre la


formation React.

Si vous ne les connaissez pas, je vous laisse suivre la formation JavaScript


Moderne disponible sur notre plateforme
(www.developpeurlibre.com/javascript-moderne) et on se retrouve dans cette
formation React juste après.

Pré-requis : Le "JavaScript moderne" 2


Mise en place de
l'environnement de
développement

Objectifs

Commençons par installer les outils et logiciels nécessaires pour le


développement d'un projet React.

Comprendre

Le système d'exploitation

Vous pouvez suivre la formation sur n'importe quel système d'exploitation.


Personnellement, je suis sur Mac OS, mais que vous soyez sur Windows ou
Linux, vous pourrez suivre toutes les étapes de chaque chapitre sans que cela
fasse la moindre différence, hormis peut-être la syntaxe de votre terminal sur
Windows, mais nous n'allons quasiment pas l'utiliser.

Node.JS et npm

Node.JS est un environnement d'exécution Javascript habituellement utilisé


pour développer des applications back-end. Or, React est du pur front-end.
Alors pourquoi installer Node.JS ?
Nous n'allons pas faire de back-end.
Node.JS inclut le gestionnaire de packages JavaScript npm. Grâce à Node.JS
et npm, nous allons pouvoir faire 2 choses qui vont nous faire gagner

Mise en place de l'environnement de développement 1


énormément de temps :

Utiliser le package "create-react-app" qui va créer tout le squelette de


notre application en incluant le support de nombreux outils pré-configurés
pour nous JSX, Babel, Webpack, etc.)

Installer n'importe quel autre package npm, ce qui va nous permettre


d'intégrer des composants open-source dans notre application, et gérer les
compatibilités de versions entre les dépendances. C'est exactement
comme le gestionnaire de paquet composer pour PHP, gems pour Ruby ou
pip pour Python.

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

Installer Node.JS et npm

Pour installer Node.JS, rendez-vous sur https://nodejs.org/fr/ et téléchargez


l'installateur. Si le site vous propose 2 versions ("Recommandé pour la plupart
des utilisateurs" ou "Dernières fonctionnalités"), prenez celle qui est
recommandée pour la plupart des utilisateurs.
Ensuite, laissez-vous guider par le processus d'installation.
Pour vérifier l'installation, ouvrez un nouveau terminal et tapez node -v : le
numéro de version de Node.JS doit s'afficher.
Le gestionnaire de packages npm est inclus dans l'installation de Node.JS :
tapez npm -v pour vérifier que vous obtenez bien un numéro de version. Ce

Mise en place de l'environnement de développement 2


numéro de version n'est pas le même que celui de Node.JS : pas de panique,
tout est normal.

Installer Visual Studio Code

Rendez-vous sur https://code.visualstudio.com/ et cliquez sur le bouton


"Download" pour le télécharger.
Ensuite, suivez le processus d'installation et ouvrez le logiciel.
Si le logiciel est en anglais et que vous le voulez en français, faites
Control+Maj+P (ou Cmd+Maj+P sur Mac), tapez "language", choisissez
"Configure Display Language", installez le français et redémarrez Visual Studio
Code.

Mise en place de l'environnement de développement 3


Nous sommes prêts à démarrer !

Mise en place de l'environnement de développement 4


Création du squelette de notre
application React

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 crée automatiquement tous les dossiers et fichiers nécessaires à la


création d'un projet React

Il inclut une commande npm start serveur de développement avec hot


reloading

Il inclut une commande npm build pour packager la version de


production de notre application

Il inclut la configuration Babel pour pouvoir utiliser JSX et ES6 sans


rien avoir à configurer

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

Il utilise l'écosystème npm qui nous permet d'installer facilement


n'importe quel package npm

Liste complète des avantages : https://github.com/facebook/create-


react-app#whats-included

Création du squelette de notre application React 1


Appliquer

Ouvrir un terminal.

Se déplacer dans le dossier où l'on veut créer notre projet React.JS

Entrer la commande npx create-react-app <nom du projet> . Par exemple, si on


appelle notre réseau social "React Network", on pourra taper npx create-
react-app react-network .

Attention, sur les toutes dernières versions de npm, la syntaxe de npx



a changé et il est possible que lorsque vous allez exécuter le
commande précédente vous ayez un message du type "Need to install
the following packages", ce qui est dommage puisque le but de passer
par npx est justement de ne pas avoir à installer un package. Si vous
êtes dans ce cas, tapez la nouvelle syntaxe : npm exec --yes create-react-
app <nom du projet>

Attendre la fin du process...

Se déplacer dans le dossier qui a été créé : cd react-network/

Taper code . pour ouvrir le dossier courant dans Visual Studio Code

Nous allons maintenant comprendre à quoi correspondent les fichiers et


dossiers qui ont été générés dans notre projet React.

Création du squelette de notre application React 2


La structure de fichiers d'un
projet React

Objectifs

Comprendre à quoi servent les fichiers et les dossiers créés.

Comprendre

Lorsque vous allez ouvrir votre projet, vous aurez peut-être des
fichiers ou des dossiers que je n'ai pas, ou inversement.

En effet, l'utilitaire create-react-app évolue en permanence, des


nouvelles fonctionnalités sont ajoutées en permanence.

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 :

La structure de fichiers d'un projet React 1


Analysons chacun des fichiers (pas forcément dans l'ordre d'apparition ni
alphabétique) :

README.md: Ce fichier contient la description du projet au format markdown.


C'est le contenu de ce fichier qui sera affiché sur votre page d'accueil
github si vous utilisez git et github (hors du cadre de cette formation)

: Il contient la version de chaque package npm installé. On y


package.json

trouve aussi des alias de commandes comme npm start , npm build , etc. qui
permettront de facilement démarrer ou builder notre application.

La structure de fichiers d'un projet React 2


package-lock.json : Il ressemble à package.json mais il garde une trace de la
version exacte installée. Par exemple on pourra avoir une version 1.0.*

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.

Dans notre repository git, on a donc un fichier package.json de quelques ko


et pas des Go de node_modules. Le projet sur git est donc de taille réduite,
rapide à téléchargé, et une fois qu'il est téléchargé sur le poste d'un
développeur, ce dernier va lancer le téléchargement des librairies listées
dans le package.json grâce à la commande npm install .

: dans le contexte d'un projet git, ce fichier va lister les fichiers et


.gitignore

dossiers de notre projet que l'on ne souhaite jamais commiter (comme par
exemple le dossier node_modules )

public: Ce dossier contient les ressources statiques de notre application


(le squelette HTML. Tout ce qui est dans ce dossier n'a rien à voir avec
React, on pourrait avoir les mêmes fichiers dans un simple projet HTML :

index.html: Le fichier html qui accueillera notre application React. Il


contient une balise <div id='root'></div> : c'est à l'intérieur de cette
balise que React va injecter notre application complète.

La structure de fichiers d'un projet React 3


favicon.ico : Le favicon de notre application

robots.txt: Un fichier pour dire aux moteurs de recherche ce qui peut


être indexé ou pas

manifest.json : Un fichier qui permet de facilement convertir notre


application en PWA (progressive web app)

logo192.png et logo512.png : Des images listées dans le manifest.json


pour une éventuelle utilisation PWA

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.

La structure de fichiers d'un projet React 4


Nettoyage du projet pour partir
de zéro

Objectifs

Nous allons maintenant supprimer l'application de démo livrée avec l'utilitaire


create-react-app afin de reprendre les concepts à partir de zéro, dans le bon
ordre.

Appliquer

Pour le fun - Avant de nettoyer

Juste avant de procéder au nettoyage, on peut essayer de lancer l'application


de démo. Pour cela :

Ouvrez le terminal dans le dossier du projet

Tapez npm start

Normalement, votre navigateur s'ouvre automatiquement à la bonne


adresse (http://localhost:3000/) et vous voyez ceci :

Nettoyage du projet pour partir de zéro 1


Même si nous n'avons pas encore commencé à apprendre React, je
vous invite à vous rendre dans le fichier App.js et à changer le texte qui
dit Edit src/App.js and save to reload . Sauvegardez vos modifications dans
le fichier App.js et jetez un oeil à votre application dans votre navigateur
: le texte a été modifié, sans avoir besoin de recharger la page. C'est le
mécanisme de hot reload, qui rend la vie de développeur beaucoup plus
facile. Chaque fois que vous modifierez un fichier dans votre projet
React, les changements apparaitront en direct.

Nettoyage du code

Supprimez tout le contenu du dossier public .


À l'instant où vous commencez à supprimer des fichiers, l'application dans
votre navigateur affiche un message d'erreur. C'est parfaitement normal. C'est

Nettoyage du projet pour partir de zéro 2


dû au hot reload dont on vient de parler : chaque modification est visible en
temps réel. Nous supprimons des fichiers référencés par d'autres fichiers, cela
génère donc une erreur. Continuons le nettoyage sans nous en occuper jusqu'à
arriver à notre état final sans erreur.

Créez un fichier index.html dans le dossier public .


Insérez-y le code suivant :

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

Puis supprimer tout le code du fichier et le remplacer par le code suivant :

function App() {
return (
<h1>Hello World</h1>
);

Nettoyage du projet pour partir de zéro 3


}
export default App;

Dans index.js :

Parmi les imports (les premières lignes de chaque fichier), supprimer


ceux qui pointent sur les fichiers qui n'existent plus (car on vient de les
supprimer)

Conservez bien import React from 'react' et import ReactDOM from 'react-dom'

s'ils sont présents.

Tout en bas du fichier, supprimez la ligne reportWebVitals(); (et les


commentaires qui la précèdent)

A ce stade, les messages d'erreurs disparaissent et on voit à nouveau


l'application.

Si vous voyez encore la première application de démo, la même qu'avant le


nettoyage, vous aurez besoin de rafraichir votre page dans le navigateur.

Une fois que votre cache est rafraîchi, vous obtiendrez ceci :

Nettoyage du projet pour partir de zéro 4


Nous avons à présent devant nous l'application React.JS la plus simple qui soit
:

Un index.html qui contient juste un <div id="root"></div>

Un App.js qui affiche <h1>Hello World</h1>

Nettoyage du projet pour partir de zéro 5


Nous allons à présent regarder par quel mécanisme le "Hello World" déclaré
dans App.js se retrouve injecté dans notre index.html .

Nettoyage du projet pour partir de zéro 6


Analyse du code react

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

Voici à quoi ressemble notre projet en l'état :

Nous avons déjà expliqué en quoi consistaient les fichiers README.md ,


package.json, package-lock.json , .gitignore , et les dossiers node_modules et public .

Analyse du code react 1


Nous allons maintenant nous intéresser au code JavaScript et React.JS : le
dossier src .

Comment le texte "Hello World" écrit dans App.js se retrouve-t-il affiché dans
le <div id="root"></div> de notre navigateur ?

Etape 1 - Chargement du HTML

Le navigateur commence par charger le contenu du fichier HTML. Ce fichier


HTML contient simplement un div root qui est vide.

Mais, dans notre fichier HTML, React.JS va automatiquement ajouter un tag


<script> qui va servir à injecter index.js dans notre page web.

Si on affiche la source du HTML, on voit ces scripts. Ils ne chargent pas


directement le fichier index.js . Il s'agit de scripts transpilés par Babel puis
packagés par Webpack qui sont automatiquement insérés dans notre DOM.
Ces scripts chargent dynamiquement le contenu de notre application.

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

Voici le contenu de index.js :

import React from 'react';


import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(

<React.StrictMode>
<App />

Analyse du code react 2


</React.StrictMode>,

document.getElementById('root')

);

Il s'agit d'un composant React un peu particulier, qui ne ressemble à aucun


autre, car son but est d'injecter notre application dans le <div id="root"></div>

de notre HTML.

Les 3 premières lignes permettent d'importer la librairie react et react-dom, qui


constituent le coeur de React.JS.

Le <React.StrictMode> permet d'activer des vérifications qui vont logguer des


messages d'avertissement et d'erreur dans notre console de navigateur si on
ne respecte pas certaines bonnes pratiques. On pourrait très bien enlever le
React.StrictMode , notre application fonctionnerait toujours. Conservez-le. Mais
dans l'idée, si on l'enlevait, se révèlerait le coeur de la fonction de notre
index.js , c'est-à-dire :

ReactDOM.render(<App />, document.getElementById('root'));

On voit ici très bien apparaître 2 arguments à la fonction :

<App /> → Quel composant charger ?

document.getElementById('root') → A quel endroit du HTML faut-il le charger ?

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 :

import App from './App';

Analyse du code react 3


C'est cette ligne qui indique à React que lorsqu'on va appeler le composant App
(en écrivant <App /> ou <App></App> , il faut aller chercher sa déclaration dans le
fichier ./App (on peut omettre le .js , cela signifie en réalité ./App.js )

Etape 3 - App.js

App est un composant React.

C'est d'ailleurs le seul véritable composant de notre application ( index.js étant


un peu à part).

Un composant est donc une fonction qui renvoie du code HTML :

function App() {
return (
<h1>Hello World</h1>
);
}
export default App;

Appeler cette fonction, c'est récupérer en retour un composant qui contient le


titre "Hello World".

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 .

Lorsqu'on connaît JavaScript et HTML, on remarque tout de suite quelque


chose :
Comment est-il possible d'écrire return <h1>Hello World</h1> ?

Analyse du code react 4


Le code HTML n'est pas dans une chaîne de caractères, ni dans une variable, il
est au beau milieu du code JavaScript.

Si j'exécute cette ligne en JavaScript habituel, j'obtiens Uncaught SyntaxError:

Unexpected token '<' .

La réponse s'appelle JSX Javascript Syntax eXtended) et nous allons nous


pencher en détail sur JSX au chapitre suivant.

Evaluer
Quizz

Si on regarde la source du fichier HTML reçu par le navigateur, que contient-


il ?

Le div ayant l'id "root" est vide

Le div ayant l'id "root" est rempli avec <h1Hello World</h1

Correction

Si on regarde la source du fichier HTML reçu par le navigateur, que contient-


il ?

✅ Le div ayant l'id "root" est vide


❌ Le div ayant l'id "root" est rempli avec <h1Hello World</h1
Si on regarde dans l'inspecteur d'éléments, on voit le h1 dans le div "root", mais
c'est uniquement car Chrome met à jour l'arbre en fonction des noeuds ajoutés,
modifiés et supprimés par JavaScript.

Analyse du code react 5


Mais si on regarde la source de la page (clic droit > afficher la source), le div
"root" du fichier HTML chargé est vide, et c'est JavaScript qui va ensuite
insérer notre contenu dans ce div dynamiquement.

Analyse du code react 6


JSX (Javascript Syntax
eXtension)

Objectifs

Maîtriser l'utilisation de la syntaxe JSX.

Comprendre

Qu'est-ce que JSX ?

JSX Javascript Syntax eXtension) est une extension de la syntaxe JavaScript


qui permet d'utiliser du code html ou des composants React.JS directement
dans du Javascript, comme s'il s'agissait de variables ou de chaînes de
caractère.

Navigateurs supportant JSX

Aucun navigateur ne supporte JSX. C'est là qu'intervient Babel. Babel permet de


convertir la syntaxe JSX en syntaxe JavaScript classique.

Pour le voir en direct :

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.

Les 2 fonctionnalités principales de JSX

JSX Javascript Syntax eXtension) 1


 JSX permet d'utiliser du code HTML comme s'il s'agissait de variables

function App() {
const titre = <h1>Bonjour</h1>
return titre
}

 JSX permet d'inclure du JavaScript au milieu du code HTML grâce aux


accolades { }

function App() {
const name = "Mathieu"
return <h1>Bonjour {name}</h1>
}

Essayons maintenant d'appliquer ces 2 principes à divers cas d'utilisations.

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

Voyons le genre de choses que l'on peut faire avec JSX :

Renvoyer un texte dans un simple div :

JSX Javascript Syntax eXtension) 2


return (
<div>Hello World</div>
);

Ajouter des enfants à ce div :

return (
<div>
<p>Un paragraphe</p>
<p>Un autre paragraphe</p>
</div>
);

Attention, avec JSX, on doit obligatoirement avoir une seule balise parente.

Ceci ne fonctionnera pas :

return (
<p>Un paragraphe</p>
<p>Un autre paragraphe</p>
);

Si on a plusieurs parents, on a le choix entre :

Encapsuler nos 2 éléments dans un div parent (comme dans


l'exemple précédent)

Encapsuler nos 2 éléments dans un Fragment explicite :

return (
<React.Fragment>
<p>Un paragraphe</p>
<p>Un autre paragraphe</p>
</React.Fragment>
);

JSX Javascript Syntax eXtension) 3


Encapsuler nos 2 éléments dans un Fragment implicite (tag vide) :

return (
<>
<p>Un paragraphe</p>
<p>Un autre paragraphe</p>
</>
);

Ajouter du code JavaScript entre accolades.


Par exemple...

Insérer une variable :

const name = "Mathieu"


return <h1>Bonjour {name}</h1>

Ou bien faire une opération mathématique :

return <h1>Huit fois deux plus dix font {8 * 2 + 10}</h1>

Ou encore insérer un morceau d'algorithme JavaScript comme ici un


opérateur ternaire :

const isConnected = true


return <h1>Vous êtes actuellement {isConnected ? "connecté" : "déconnecté"}</h1>

Bref, ce qui est entre accolades est réellement exécuté comme du code
JavaScript !

JSX Javascript Syntax eXtension) 4


Stocker un composant react ou un arbre HTML dans une variable :

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>
)
}

Fonctionne aussi avec plusieurs tags imbriqués :

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>
}

JSX Javascript Syntax eXtension) 5


</div>
)
}

N'afficher un élément que si une condition est vérifiée

On pourrait faire :

function App() {
const isConnected = true
return (
<div>
<h2>Bienvenue</h2>
{isConnected ? <button>Se déconnecter</button> : null}
</div>
)
}

Mais il existe un raccourci avec && :

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

JSX Javascript Syntax eXtension) 6


App.js à son état initial, qui était celui-ci :

function App() {
return (
<h1>Hello World</h1>
)
}
export default App;

Evaluer
Quizz

Qu'est-ce que JSX ?

Une boîte à outils permettant d'accéder à des fonctions utilitaires, comme


jQuery ou lodash

Une facilité de codage qui permet de manipuler des balises HTML et des
composants React comme des variables

Quelles sont les 2 contraintes principales de JSX ?

Nous allons devoir faire des configurations babel et webpack complexes

On ne peut pas utiliser l'attribut class sur les balises HTML, il faut utiliser
className

On ne peut pas avoir 2 balises côte-à-côte au plus haut niveau du morceau


de code JSX

On ne peut jamais avoir 2 balises côte-à-côte, quel que soit le niveau

Correction

Qu'est-ce que JSX ?

JSX Javascript Syntax eXtension) 7


❌ Une boîte à outils permettant d'accéder à des fonctions utilitaires, comme
jQuery ou lodash.
✅ Une facilité de codage qui permet de manipuler des balises HTML et des
composants React comme des variables.

Quelles sont les 2 contraintes principales de JSX ?


❌ Nous allons devoir faire des configurations babel et webpack complexes
✅ On ne peut pas utiliser l'attribut class sur les balises HTML, il faut utiliser
className

✅ On ne peut pas avoir 2 balises côte-à-côte au plus haut niveau du morceau


de code JSX
❌ On ne peut jamais avoir 2 balises côte-à-côte, quel que soit le niveau
Pas de problème de configuration babel et webpack car nous avons utilisé
create-react-app qui masque cette complexité pour nous.

JSX Javascript Syntax eXtension) 8


Créer notre premier
composant

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

🤔 Si on affiche la page web de YouTube, on voit très rapidement que


des éléments graphiques semblent se répéter : les vidéos sont toutes
affichées selon le même format, les tags ont tous un aspect
identiques, les menus sur la gauche également... C'est ça un
composant : un élément graphique qui a une fonction précise et
unitaire.

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.

Créer notre premier composant 1


On va écrire tout notre code HTML, JavaScript et CSS dans un même
composant indépendant, ce qui permettra même potentiellement de copier ce
composant pour l'utiliser dans un autre projet.

La plupart du temps, un composant contient d'autres composants, c'est donc


plutôt un arbre de composants que l'on va exporter, mais cet arbre étant
indépendant du reste de l'application, on va pouvoir le copier/coller dans un
autre projet.

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.

Un composant contient quasiment toujours un ou plusieurs autres composants,


une application React est donc un arbre de composants React mixés avec du
code HTML.

Appliquer

Le but de notre réseau social est d'afficher des posts.

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 notre premier composant 2


Puis nous allons exporter ce composant, afin de l'utiliser dans notre App.js et
l'afficher à l'écran.

Créer le composant

Comme tout composant, ce composant doit déclarer une fonction qui retourne
le code HTML de notre post.

On commence donc par déclarer une fonction Post :

function Post() {

On ajoute immédiatement l'export afin de pouvoir utiliser ce composant depuis


d'autres composants :

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;

Notre composant Post est terminé !

Nous allons maintenant l'afficher dans notre application.

Créer notre premier composant 3


Utiliser notre composant

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 :

import Post from './Post'

(le .js n'est pas obligatoire)

Puis, nous allons l'insérer là où on souhaite dans notre HTML, par exemple :

import Post from './Post'

function App() {
return (
<>
<h1>Fil d'actualité :</h1>
<Post />
</>
);
}
export default App;

Créer notre premier composant 4


Note: <Post /> est équivalent à <Post></Post>

Ce composant étant réutilisable, nous pouvons l'insérer plusieurs fois, par


exemple ici nous ajoutons 3 fois le composant Post à notre application :

return (
<>
<h1>Fil d'actualité :</h1>
<Post />
<Post />
<Post />
</>
);

Créer notre premier composant 5


On peut utiliser la balise paragraphe ( <p> ) au lieu de <div> pour un aspect plus
aéré :

function Post() {
return <p>Bonjour tout le monde !</p>
}
export default Post;

Créer notre premier composant 6


Pour cerner l'aspect réutilisable des composants, on peut s'amuser à ajouter un
emoji de message devant notre texte : on le définit une seule fois, et ensuite,
qu'il y ait 1 post, 3 posts ou 100 posts à l'écran, tous les posts auront la même
apparence, avec le fameux emoji, car c'est le même composant qui réalise
l'affichage :

💬
function Post() {
return <p> Bonjour tout le monde !</p>
}
export default Post;

Créer notre premier composant 7


Mais pour que le composant soit complètement réutilisable, il faudrait pouvoir
paramétrer son contenu dynamiquement, plutôt que de coder en dur "Bonjour
tout le monde".

Pour cela, nous allons avoir besoin des props.

Evaluer
Quizz

Comment implémente-t-on un composant ?

Une fonction qui renvoie du HTML en tant que code JSX

Créer notre premier composant 8


Une fonction qui renvoie du HTML en tant que chaîne de caractère

Correction

Comment implémente-t-on un composant ?


✅ Une fonction qui renvoie du HTML en tant que code JSX
❌ Une fonction qui renvoie du HTML en tant que chaîne de caractère

Créer notre premier composant 9


Les props

Objectifs

Nous sommes capables de créer des composants et de les utiliser dans


d'autres composants.

Mais pour l'instant, leur contenu est "codé en dur". Notre composant Post , par
exemple, affiche toujours "Bonjour tout le monde".

Nous allons maintenant rendre le texte du post configurable depuis le


composant parent.

Comprendre

Faisons une analogie avec le langage HTML :

En HTML, si on souhaite écrire "Bonjour" dans un text input HTML, on va écrire


:

<input type="text" value="Bonjour" />

Avec React.js, c'est exactement le même fonctionnement !

On va pouvoir passer des informations à notre composants via des attributs.

Avec React, que l'on appelle props dans React.

Si l'on souhaite que notre Post affiche "Comment allez-vous", on va simplement


passer cette information dans une prop (un attribut donc), que l'on appellera

Les props 1
comme on le souhaite. Par exemple, pour le texte de mon post, je peux appeler
la prop text , cela donnera donc :

<Post text="Comment allez-vous ?" />

Du côté de notre composant Post , nous pouvons accéder à la valeur en faisant


simplement props.text . Il ne nous reste plus qu'à l'afficher où on le souhaite
dans notre HTML !

Appliquer

Modifions notre App afin de passer 3 textes différents à nos 3 posts :

Etape 1 : Envoyer les valeurs au composant Post

function App() {
return (
<>
<h1>Fil d'actualité :</h1>
<Post text="Bonjour tout le monde !" />
<Post text="Comment allez-vous ?" />
<Post text="Vive React.JS" />
</>
);
}

Pour l'instant, il ne se passe rien.


En effet, notre composant Post est toujours codé pour renvoyer "Bonjour tout
le monde" en dur.

Etape 2 : Récupérer ces valeurs dans le composant


Post

Les props 2
Nous allons donc faire en sorte qu'il affiche la valeur passée en attribut (en
React on parlera de props).

Pour cela, nous allons ajouter props en argument de notre fonction :


function Post() devient function Post(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

(puisqu'on a nommé notre attribut 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.

Cela donne donc :

💬
function Post(props) {
return <p> {props.text}</p>
}
export default Post;

Et nous obtenons le résultat souhaité :

Les props 3
A retenir

Nos composants sont maintenant réellement réutilisables car un composant


peut avoir une structure fixe, mais un contenu personnalisable directement.

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

Un composant A passe à un composant B une information (le prénom d'un


utilisateur) via une prop "prenom". Quel code permettra au composant B
d'afficher le prénom à l'écran ?

<div>{prenom}</div>

<div>prenom</div>

<div>{props.prenom}</div>

<div>props.prenom</div>

Correction

Un composant A passe à un composant B une information (le prénom d'un


utilisateur) via une prop "prenom". Quel code permettra au composant B
d'afficher le prénom à l'écran ?

❌ <div>{prenom}</div> Afficherait une erreur "prenom is not


defined"

❌ <div>prenom</div> Afficherait le texte "prenom" à l'écran

✅ <div>{props.prenom}</div> Correct

❌ <div>props.prenom</div> Afficherait le texte "props.prenom" à


l'écran

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

Ce chapitre va être un pot-pourri de tout un tas de manipulations avec les


props :

Voici les différentes manipulations que nous allons explorer :

Comment passer une prop en chaine de caractères (rappel)

Comment passer plusieurs props

Comment passer cette même prop (chaîne de caractères) si sa valeur est


stockée dans une variable

Comment passer un nombre en prop

Les props sous toutes les coutures 1


Utiliser la décomposition (destructuring en anglais) pour créer une variable
par props

Utiliser les defaultProps pour définir des valeurs par défaut

Utiliser les propTypes pour typer nos props et/ou les rendre obligatoires

Les différentes manières de passer plusieurs props à un composant :

Plusieurs props séparées avec assignation manuelle

Plusieurs props séparées avec une seule assignation en spread

Une seule prop avec un objet

Evaluer
Quizz

Comment passer un tableau contenant les chiffres 10, 45 et 90 à un


composant dans une prop nommée tab ?

<Composant tab="1, 2, 3" />

<Composant tab=[1, 2, 3] />

<Composant tab={"1, 2, 3"} />

<Composant tab={[1, 2, 3]} />

Correction

Un composant A passe à un composant B une information (le prénom d'un


utilisateur) via une prop "prenom". Quel code permettra au composant B
d'afficher le prénom à l'écran ?

Les props sous toutes les coutures 2


❌ <Composant tab="1, 2, 3" />

Enverrait la chaîne de caractère "1, 2, 3"


❌ <Composant tab=[1, 2, 3] />

Erreur de compilation, une prop ne peut être qu'une chaîne de caractères ou


bien un bloc JavaScript entre accolades

❌ <Composant tab={"1, 2, 3"} />

Enverrait la chaîne de caractère "1, 2, 3"


✅ <Composant tab={[1, 2, 3]} />

Correct

Les props sous toutes les coutures 3


Les props - attributs vs enfants

Objectifs

Dans ce chapitre, nous allons voir comment passer des enfants à notre
composant, toujours avec le mécanisme des props.

Comprendre

Attributs VS Contenu du noeud

Faisons une analogie avec le langage HTML :

En HTML, il existe 2 manières de passer une information à un élément :

 Via un attribut
Par exemple, si on souhaite écrire "Bonjour" dans un text input HTML, on va
devoir écrire :

<input type="text" value="Bonjour" />

On a donc passé "Bonjour" via un attribut.

Les props - attributs vs enfants 1


 Via le contenu du noeud
Par exemple, si on souhaite écrire "Bonjour" dans une textarea HTML, on va
devoir écrire :

<textarea>Bonjour</textarea>

On a donc passé "Bonjour" via le contenu du noeud.

Ces 2 éléments HTML aux fonctionnalités si proches (un textarea est


comparable à un text input en version multilignes) utilisent 2 manières
distinctes de passer l'information, cela montre à quel point le standard est
ouvert à ce sujet.
On peut donc librement opter pour une option ou l'autre, selon les cas.

En React, c'est pareil, nous pourrons envisager de passer les informations à un


composant enfant avec ces 2 manières, au choix :

<Video title="La relativité générale" />

<Video>La relativité générale</Video>

Comment choisir la meilleure implémentation ?


Nous le verrons dans quelques minutes.
Pour l'instant, voyons comment implémenter la version avec le passage par
noeud enfant.

Les props - attributs vs enfants 2


Appliquer

Rappel sur le passage de props par attribut

Voici comment nous avions pu passer 3 textes différents à nos 3 posts :

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;

Passer une valeur via le contenu du noeud

Modifions notre App afin de passer 3 textes différents à nos 3 posts :

function App() {
return (
<>
<h1>Fil d'actualité :</h1>
<Post>Bonjour tout le monde !</Post>
<Post>Comment allez-vous ?</Post>

Les props - attributs vs enfants 3


<Post>Vive React.JS</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.

Pour cela, il suffit de lire la valeur de props.children .

props.children permet au composant de récupérer tout ce qui a été passé entre


sa balise ouvrante et sa balise fermante.

Nous allons donc remplacer {props.text} par {props.children} .

Cela donne donc :

💬
function Post(props) {
return <p> {props.children}</p>
}
export default Post;

Et nous obtenons le même résultat :

Les props - attributs vs enfants 4


Comment choisir entre les deux ?

Le cas général est de passer les props par attribut.

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

Il s'agit donc de composants de style "wrapper", qui fournissent une


fonctionnalité de haut niveau qui encadre le contenu, en laissant le
développeur qui utilise ce composant mettre ce qu'il veut à l'intérieur.
Un exemple simple pourrait être un composant Warning qui affiche une bordure
orange autour du contenu du noeud. Tout ce qui sera inséré entre la balise
ouvrante et la balise fermante du composant Warning sera donc entouré de
orange.

Les props - attributs vs enfants 5


Nous verrons plus tard comment ce type de composants permet de créer des
mises en pages (layouts) : un composant pour le header, un composant pour le
contenu principal, un composant pour la sidebar, qui seront de simples
wrappers dans lesquels on va injecter notre contenu.

Evaluer
Quizz

Le composant Comp est appelé avec le code suivant : <Comp

message="Salut">Bonjour</Comp> . On veut afficher "1 Salut, 2Bonjour". Quel est le


bon code ?

return `1: ${props.children}, 2: ${props.message}`

return `1: ${props.message}, 2: ${props.children}`

return `1: ${props.message}, 2: ${props.bonjour}`

Correction

Le composant Compest appelé avec le code suivant : <Comp


message="Salut">Bonjour</Comp> . On veut afficher "1 Salut, 2Bonjour". Quel est le

bon code ?
❌ return `1: ${props.children}, 2: ${props.message}`

Affiche "1 Bonjour, 2 Salut"


✅ return `1: ${props.message}, 2: ${props.children}`

Correct

❌ return `1: ${props.message}, 2: ${props.bonjour}`

Affiche "1 Salut, 2 undefined"

Les props - attributs vs enfants 6


React Dev Tools

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 .

Nous allons pour le moment regarder uniquement l'onglet Components.

Cet onglet est très utile puisqu'il permet de visualiser l'arbre de nos
composants :

React Dev Tools 1


Il est même possible d'aller plus loin que ça et cliquer sur un composants pour
modifier ses props en temps réel et visualiser les changements en direct sur
notre page :

React Dev Tools 2


Lors de phases de debug, cet outil se montrera très utile.

React Dev Tools 3


Générer une liste de
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} />)
}

Cette approche fonctionne, mais nous avons 3 inconvénients à résoudre :

 Un warning apparaît dans la console du navigateur

 Cela multiplie les lignes de code, contrairement aux outils de


programmation fonctionnelle

Générer une liste de composants 1


 La logique de construction de la variable et son rendu sont séparés, ce qui
complexifie la compréhension du code

Corrigeons ces 3 inconvénients :

Le warning "Each child in a list should have a unique


"key" prop"

Le warning suivant est présent dans la console :

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 .

La key permet à React de savoir, lorsque la liste change, quel élément de la


liste "avant changement" correspond à quel élément de la liste "après
changement", pour pouvoir les garder intacts.

Générer une liste de composants 2


Cette démonstration très parlante faite par un membre de la communauté
React va nous aider à le comprendre : https://jsbin.com/wohima/edit?js,output
La règle est donc que l'identifiant passé en tant que key doit être un identifiant
fixe, qui ne doit jamais changer même si on ajoute, supprime ou modifie des
données (ce qui n'est pas le cas de l'indice d'une boucle).

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} />)
}

Le warning a disparu dans la console.

Utiliser map() plutôt qu'une boucle for

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

La programmation fonctionnelle est une méthode de programmation qui


permet d'éviter les effets de bords, et qui, en plus de cela, donne des syntaxes
plus concises.

Générer une liste de composants 3


Avant :

let postsComponents = []
for (const p of posts) {
postsComponents.push(<Post key={p.id} postData={p} />)
}

Après :

const postsComponents = posts.map(p => <Post key={p.id} postData={p} />)

Faire notre boucle au moment du rendu

Plutôt que de créer une variable postsComponents dans le corps de notre


composant, et de l'utiliser dans le render, nous allons directement faire notre
map() à l'endroit où le rendu des composants a lieu, afin encore une fois de
simplifier la syntaxe.

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} />)}
</>
);
}

Générer une liste de composants 4


Evaluer
Quizz

Quel est le meilleur choix pour l'attribut key ?

Passer l'incrément de la boucle

Passer un identifiant qui est une caractéristique intrinsèque de la donnée

Correction

Quel est le meilleur choix pour l'attribut key ?


❌ Passer l'incrément de la boucle
Faux, cela va poser des problèmes d'affichage lors de la mise à jour.
✅ Passer un identifiant qui est une caractéristique intrinsèque de la donnée
Correct

Générer une liste de composants 5


Les événements

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() {

let title = 'React Network'

Les événements 1
return (
<h1>{title}</h1>
)

export default Header

Dans notre composant, nous allons créer un bouton :

<button>Cliquez ici</button>

Créons également une fonction qui correspond au code que nous souhaitons
exécuter lorsque le bouton est cliqué :

const onClickHandler = () => {


console.log("Bouton 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 :

<button onClick={onClickHandler}>Cliquez ici</button>

C'est tout !

Le message "Bouton cliqué" s'affiche dans la console lorsqu'on clique sur le


bouton.

On est donc en mesure de détecter les interactions avec l'utilisateur au sein


d'un composant :

Les événements 2
function Header() {

let title = 'React Network'

const onClickHandler = () => {


console.log("Bouton cliqué");
}

return (
<div>
<h1>{title}</h1>
<button onClick={onClickHandler}>Cliquez ici</button>
</div>
)

export default Header

Il existe tous les événements habituels : onChange, onFocus, onClick, etc.

Par exemple, en modifiant le h1 comme ceci :

<h1 onMouseEnter={onMouseEnterHandler} onMouseLeave={onMouseLeaveHandler}>{title}</h1>

Et en créant les 2 fonctions suivantes :

const onMouseEnterHandler = () => {


console.log("Souris sur le titre");
}

const onMouseLeaveHandler = () => {


console.log("Souris éloignée");
}

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)

Mais comment passer des arguments ?

Les 2 syntaxes pour passer des fonctions

La syntaxe

onClick={maFonction}

Est équivalente à :

onClick={() => maFonction()}

Du coup, si on souhaite passer des arguments à une fonction, on utilisera


toujours la 2eme version. Par exemple :

onClick={() => sayHello('Vincent')}

Exemple :

function Header() {

let title = 'React Network'

const sayHello = (name) => {


console.log(`Bonjour ${name}`);
}

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

A noter sur les événements : par défaut, si on ne passe pas d'arguments à


notre fonction, un argument que l'on appelle souvent e , ev ou event sera
passé à notre fonction.

Celui-ci contient des données intéressantes comme la position (x,y) de la


souris au moment de l'événement, une référence à l'élément HTML étant la
cible de l'événement ( e.target ), etc...

Les événements 5
Le state et le hook useState

Objectifs

La notion de state (état) est le concept central de React.

Nous ne l'avons pas vu jusqu'ici car notre page est statique, sans la moindre
interaction.

Cela va à présent changer, nous allons implémenter des interactions, et toute


interaction dans React repose sur la notion de state.

React utilise une logique de programmation déclarative.

Ce type de programmation utilise souvent une notion d'état (c'est le cas de


React).

Voyons en quoi consiste la programmation déclarative, et en quoi cela introduit


naturellement le concept de state (état) qui va nous aider à créer des
applications d'une manière très simple et élégante..

Comprendre

La programmation à laquelle nous sommes habitués est la programmation


impérative.

Par exemple, pour implémenter un compteur simple qui représente une


température et qui change de couleur 19°C ou moins ⇒ froid ⇒ bleu / 20°C ou
plus ⇒ chaud ⇒ rouge) :

Le state et le hook useState 1


Voici le pseudo algorithme qui permettrait de le faire en programmation
classique (impérative) :

- Lors d’un changement de valeur :

- Si la valeur est ≥ à 20, alors :


- Enlever le fond bleu
- Ajouter le fond rouge

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

Avec la programmation déclarative, on est sur une autre philosophie :

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 state et le hook useState 2


Deuxième différence : on n'est pas obligé de lister les événements qui sont la
cause du changement de l'affichage (un changement de valeur, l'initialisation
de l'application, etc...). Ceci présente un énorme avantage : si la valeur est
modifiée par un nouvel événement que nous n'avions pas prévu à la base, dans
le cas impératif on a un bug, alors que dans le cas déclaratif cela fonctionne
directement.

Dans la version déclarative, on voit que l'affichage dépend de ce morceau de


phrase : "Si la valeur est ≥ 20". C'est cette condition qui sera notre état, notre
state.

Le state joue 2 rôles :

Le rôle de décideur : c'est lui qui détermine comment sera affiché notre
composant (rouge ou bleu)

Le rôle de déclencheur : React va synchroniser l'affichage de notre page


avec le state du composant. Cela signifie que dès que je vais modifier le
state, mon composant va de manière presque magique être mis à jour à
l'écran avec la bonne couleur.

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

Approche naïve (qui ne fonctionne pas)

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

Vous allez probablement faire quelque chose du style :

Le state et le hook useState 3


<button onClick={changeTitle}>Cliquez ici</button>

Avec une fonction changeTitle qui modifie la valeur de la variable title :

const changeTitle = () => {


title = "Mon nouveau titre"
}

(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 :

import React, { useState } from 'react'

Puis nous allons l'utiliser ainsi :

const [title, setTitle] = useState('React Network')

Le state et le hook useState 4


Nous voyons 3 choses importantes :

La dernière, "React Network" est la valeur initiale de notre variable. Lorsque


le composant est affiché pour la première fois, la variable vaut "React
Network"

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.

Le code final est donc :

import React, { useState } from 'react'

function Header() {

const [title, setTitle] = useState('React Network')

const changeTitle = () => {


setTitle("Mon nouveau titre")
}

return (
<div>
<h1>{title}</h1>
<button onClick={changeTitle}>Cliquez ici</button>
</div>
)

export default Header

Cela fonctionne : si on clique sur le bouton, on voit bien la nouvelle valeur


apparaître à l'écran !

Implémenter le bouton "J'aime"

Le state et le hook useState 5


Nous allons maintenant implémenter le bouton "J'aime" sur nos posts.

Commençons par ajouter un bouton "J'aime" :

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 ) :

const [nbLikes, setNbLikes] = useState(props.postData.likes)

Créons la fonction qui incrémente le nombre de likes :

const likePost = () => {


setNbLikes(nbLikes + 1)
}

Il ne reste plus qu'à l'appeler depuis l'événement onClick du bouton :

<button onClick={likePost}>J'aime</button>

Nous avons bien le nombre de likes qui augmente lorsqu'on clique sur un
bouton !

Le state et le hook useState 6


Problème à corriger : on peut augmenter le nombre de likes autant qu'on le
souhaite.

Il va falloir implémenter une logique supplémentaire : si je n'ai jamais liké le


post, je fais 1 like, mais si j'ai déjà liké le post, j'enlève mon like et je fais donc
1 sur les likes.

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) :

const [isLiked, setIsLiked] = useState(false)

Modifions la logique de notre fonction : si je n'ai jamais liké ⇒ 1, si j'ai déjà liké
⇒ 1 :

const likePost = () => {


const increment = isLiked ? -1 : 1
setNbLikes(nbLikes + increment)
}

Mais il faut aussi penser à changer l'état de isLiked :

const likePost = () => {


const increment = isLiked ? -1 : 1
setNbLikes(nbLikes + increment)
setIsLiked(!isLiked)
}

Enfin, au niveau du bouton, on va changer le texte affiché : "J'aime" si je n'ai


jamais liké ce post, et "Vous aimez ce post" dans le cas contraire :

<button onClick={likePost}>{isLiked ? "Vous aimez ce post" : "J'aime"}</button>

Le state et le hook useState 7


Et voilà ! Nous avons un bouton like fonctionnel !

Attention cependant, ceci est simplement un exercice isolé. Nous avons 2


endroits où le nombre de likes est stocké : la variable posts de notre
composant App , et le state de chaque Post . Cela n'est pas propre et dans la
version finale de notre application, tout sera géré dans un seul et même state.

Evaluer
Quizz

Quelles sont les 2 affirmations qui représentent le mieux ce qu'est le state


d'un composant ?

Une variable qui ne peut pas changer de valeur

Une variable qui cause une actualisation du composant à l'écran dès que sa
valeur change

Une variable qui va décider de l'apparence (couleurs, texte, styles,


éléments) de notre composant

Quelle valeur nous est retournée lorsqu'on appelle le hook useState

Un objet avec 2 propriétés

Un tableau contenant 2 éléments

Rien

Correction

Le state et le hook useState 8


Quelles sont les 2 affirmations qui représentent le mieux ce qu'est le state
d'un composant ?
❌ Une variable qui ne peut pas changer de valeur
✅ Une variable qui cause une actualisation du composant à l'écran dès que sa
valeur change
✅ Une variable qui va décider de l'apparence (couleurs, texte, styles,
éléments) de notre composant

Quelle valeur nous est retournée lorsqu'on appelle le hook useState


❌ Un objet avec 2 propriétés
✅ Un tableau contenant 2 éléments
Correct. Lorsqu'on a const [state, setState] = useState('') , useState nous renvoie
un tableau avec 2 éléments (le premier est state et le 2eme est setState).
❌ Rien

Le state et le hook useState 9


Supprimer un post (depuis le
composant de plus haut
niveau)

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

Nous voulons que l'ajout ou la suppression d'un post provoque une


actualisation de notre composant à l'écran. Nous avons donc besoin d'ajouter
notre liste de posts dans le state.

Ensuite, il suffira de supprimer un élément de notre liste lors du clic sur le


bouton pour voir en temps réel le post disparaître de l'écran.

Nous allons ajouter notre liste de posts au state :

const [posts, setPosts] = useState(initialPosts)

Supprimer un post (depuis le composant de plus haut niveau) 1


En parallèle, nous allons ajouter un bouton sur notre page afin de supprimer le
post ayant l'id 90.

<button onClick={() => deletePost(90)}>Supprimer un post</button>

Il ne nous reste plus qu'à mettre à jour la variable posts indirectement grâce à
la méthode setPosts :

const deletePost = (id) => {


setPosts(posts.filter(p => p.id != 90))
}

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

Supprimer un post (depuis le composant de plus haut niveau) 2


Correction

Recopie de la question

Correction (indiquer par ✅ ou ❌ la bonne ou mauvaise réponse)

Pour aller plus loin

Supprimer un post (depuis le composant de plus haut niveau) 3


Modifier le state depuis un
composant enfant

Objectifs

Afin de supprimer un post depuis le composant Post lui-même, nous allons


devoir apprendre comment un composant A peut modifier le state d'un
composant B B étant le parent de A.

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

Ce qu'il ne faut surtout pas faire

Une méthode qui fonctionnerait serait de déplacer deletePost dans le


composant Post, et de passer 2 props ( posts et setPosts ) à Post, afin que nous
puissions utiliser props.posts et props.setPosts ) dans Post.

Modifier le state depuis un composant enfant 1


Pourquoi il ne faut surtout pas le faire :

Première raison : la maîtrise du state :


Si on passe un setter (ici il s'appelle setPosts, mais on parle de n'importe quel
setter renvoyé par useState ) à un composant, ce composant peut lui-même le
passer à d'autres composants, et on perd complètement la maîtrise de notre
state : n'importe quel composant de l'application peut modifier l'état de notre
composant avec n'importe quelle valeur.

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.

Deuxième raison : la cohérence du composant :


La logique de React est de concevoir des composants qui fonctionnent, autant
que possible, de manière autonome. On lui fournit une liste de props, et il vie sa
vie (son state évolue, etc...)
Si un composant qui s'appelle "Post", dont le rôle est d'afficher un post, a
besoin, pour fonctionner correctement, de la liste de tous les posts de notre
application, on voit bien que quelque chose ne tourne pas rond.
Pourquoi, pour afficher un post donné, aurais-je besoin de connaître la liste de
tous les autres posts ? Il n'y a pas d'explication logique. Ce genre de choses
doit faire tilt : on est sur une mauvaise voie.

Modifier le state depuis un composant enfant 2


En résumé : ne jamais passer setPosts (le setter du useState), passer des
fonctions "utilitaires" comme addPost, deletePost, likePost, ... Comme s'il
s'agissait d'une API.

Ce qu'il faut faire

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.

Ainsi, la manipulation du tableau de posts reste à la maîtrise du composant


App.

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

Puisque notre bouton de suppression va devoir être partie intégrante du Post,


nous pouvons d'ores et déjà déplacer la balise button depuis App vers le
composant Post.

En l'état, nous avons un problème :

Notre bouton fait appel à deletePost mais cette fonction est définie dans le
composant App

Même si on déplaçait deletePost dans le composant Post, cela ne


fonctionnerait pas car deletePost a besoin d'accéder à posts et setPosts .

On a donc 3 choses à implémenter :

Modifier le state depuis un composant enfant 3


1. Créer la fonction utilitaire dans le composant App :

const deletePost = (id) => {


setPosts(posts.filter(p => p.id != id))
}

2. La passer en props au composant Post :

<Post key={p.id} postData={p} deletePost={deletePost} />

(on ne passe ni posts ni setPosts , ce qui est parfait)

3. Appeler cette méthode lorsque l'utilisateur clique sur le bouton :

<button onClick={() => props.deletePost(props.postData.id)}>Supprimer le post</button>

Evaluer
Quizz

Que passe-t-on au composant enfant pour qu'il puisse modifier le state du


composant parent ?

On passe le setter du state en tant que prop

On ne peut pas le faire

On passe une fonction utilitaire en tant que prop

Modifier le state depuis un composant enfant 4


Correction

Que passe-t-on au composant enfant pour qu'il puisse modifier le state du


composant parent ?

❌ On passe le setter du state en tant que prop


Cela fonctionnerait, mais serait dangereux (le composant parent perd la
maîtrise de son état)

❌ On ne peut pas le faire


✅ On passe une fonction utilitaire en tant que prop

Modifier le state depuis un composant enfant 5


Le "props drilling" et le flux
d'information ("data down,
events up")

Objectifs

Le but de ce chapitre est de comprendre quel est le flux des données


échangées entre les différents composants d'une application React.

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.

Mais on pourrait imaginer qu'un plus grand nombre de composants


intermédiaires se trouvent entre le composant dans lequel est stocké l'état et
celui qui utilise cette information.

Dans ce cas là, nous allons devoir passer les props de composant en
composant, c'est ce que l'on appelle le props drilling.

Les données sont toujours transmises de composant parent à composant


enfant, c'est à sens unique : on dit "DATA DOWN".
Par contre, les enfants peuvent demander à modifier ce state à leur parent, via
un événement comme un clic sur un bouton. C'est le cas de notre Post qui
demande au composant parent App de supprimer le post ayant tel identifiant.

Le "props drilling" et le flux d'information ("data down, events up") 1


Les événements remontent donc de l'enfant vers le parent. On dit donc
"EVENTS UP".

Appliquer

Nous allons passer de l'architecture suivante :

App

Post

À l'architecture suivante :

App

Feed (flux d'actualité)

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.

Inversement, lors de l'événement "clic sur le bouton supprimer du post",


l'événement va remonter (c'est une image, en réalité rien ne remonte c'est la
référence qui était descendue) à partir de PostDeleteButton jusqu'à App où se
trouve la fonction deletePost.

Important : le props drilling a ses limites :

Le "props drilling" et le flux d'information ("data down, events up") 2


 Lorsque le nombre de niveaux intermédiaires devient trop grand : faire du
props drilling sur 10 niveaux rend le code complexe à maintenir.

 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.

Lorsqu'une de ces 2 limites est atteinte, on va mettre d'autres mécanismes en


place pour gérer un état applicatif global, qui n'a pas besoin d'être passé de
niveau en niveau, nous verrons cela plus tard.

Evaluer
Quizz

Question en gras

Réponse 1

etc

Question en gras

Correction

Recopie de la question

Correction (indiquer par ✅ ou ❌ la bonne ou mauvaise réponse)

Le "props drilling" et le flux d'information ("data down, events up") 3


Pour aller plus loin

Le "props drilling" et le flux d'information ("data down, events up") 4


Implémenter un "empty state"
(+ importer des images et
utiliser l'opérateur ternaire)

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

Implémenter un "empty state" (+ importer des images et utiliser l'opérateur ternaire) 1


Par exemple, me concernant, je n'ai jamais uploadé de vidéos sur Youtube avec
mon compte personnel. Du coup, si je vais dans "Ma chaîne", je vois ceci :

Les anglais appellent ce genre d'écran un "empty state", c'est-à-dire un écran


d'état vide. Si j'avais simplement un écran vide (car je n'ai aucune vidéo), ce
serait beaucoup moins clair pour moi. Un empty state est donc un pattern très
commun dans les applications web, et il est très utile d'un point de vue
marketing car il incite l'utilisateur, grâce à un bouton d'appel à l'action, de faire
une action dans votre logiciel (ici, mettre en ligne une vidéo).

Implémenter un "empty state" (+ importer des images et utiliser l'opérateur ternaire) 2


Le empty state de "Le bon coin" :

Développons maintenant notre propre empty state.


On va donc avoir une logique qui enveloppe notre composant dans sa globalité,
et qui dit : si posts est vide, afficher "Aucun post pour le moment", sinon
afficher la liste des posts.

Appliquer

Préparation

Commençons par mettre en commentaire le code actuel afin de développer


notre écran vide sans être pollué par le reste :

Implémenter un "empty state" (+ importer des images et utiliser l'opérateur ternaire) 3


{/*
<h3>Fil d'actualité :</h3>
{posts.map(p => <Post key={p.id} postData={p} deletePost={deletePost} />)}
*/}

Ajoutons un message centré horizontalement disant qu'il n'y a aucun post à


afficher :

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>
</>
)

Implémenter un "empty state" (+ importer des images et utiliser l'opérateur ternaire) 4


Afficher une image

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 :

Implémenter un "empty state" (+ importer des images et utiliser l'opérateur ternaire) 5


Téléchargez l'image de votre choix en PNG (nous verrons juste après pour les
SVG et placez la dans un nouveau dossier src/images.

Dans notre composant, nous pouvons importer l'image :

import emptyImage from '../images/empty.png'

Pour l'afficher dans notre composant, il ne reste plus qu'à faire :

<img width="300" src={emptyImage} />

(nous verrons plus tard comment utiliser l'attribut style pour injecter du CSS

Implémenter un "empty state" (+ importer des images et utiliser l'opérateur ternaire) 6


Si vous voulez importer un SVG, vous pouvez utiliser l'import ReactComponent qui
permet de convertir un fichier SVG en composant React, comme ceci :
import:

import { ReactComponent as EmptyImageSvg } from '../images/empty.svg'

utilisation:

<EmptyImageSvg width="300" height="300" />

Implémenter un "empty state" (+ importer des images et utiliser l'opérateur ternaire) 7


L'opérateur ternaire

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.

Avec l'opérateur ternaire, on peut faire ceci très facilement :

import { useState } from 'react'


import Post from './Post'
import emptyImage from '../images/empty.png'

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) {

const [posts, setPosts] = useState(initialPosts)

const deletePost = (id) => {


setPosts(posts.filter(p => p.id != id))
}

const contentPosts = <>


<h3>Fil d'actualité :</h3>
{posts.map(p => <Post key={p.id} postData={p} deletePost={deletePost} />)}
</>

const contentEmpty = <div align="center">


<img width="300" src={emptyImage} />
<h3>Aucun post pour le moment</h3>
</div>

return posts.length > 0 ? contentPosts : contentEmpty

export default Feed

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 .

Implémenter un "empty state" (+ importer des images et utiliser l'opérateur ternaire) 8


Mais une manière beaucoup plus répandue est de ne pas créer de variables et
d'afficher tout le détail dans le return :

import { useState } from 'react'


import Post from './Post'
import emptyImage from '../images/empty.png'

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) {

const [posts, setPosts] = useState(initialPosts)

const deletePost = (id) => {


setPosts(posts.filter(p => p.id != id))
}

return posts.length > 0 ?


(
<>
<h3>Fil d'actualité :</h3>
{posts.map(p => <Post key={p.id} postData={p} deletePost={deletePost} />)}
</>
) : (
<div align="center">
<img width="300" src={emptyImage} />
<h3>Aucun post pour le moment</h3>
</div>
)

export default Feed

Rappelons ici la différence entre code impératif (classique) et code déclaratif


(comme React).
Dans du code impératif, la philosophie est : si un post est supprimé, alors je
regarde combien il en reste, s'il en reste zéro alors je supprime la liste des
posts et j'affiche "Aucun post pour le moment".
Dans du code déclaratif comme le nôtre, on va décrire toutes les possibilités,
du style : je veux un composant qui se comporte de la manière suivante : s'il y a

Implémenter un "empty state" (+ importer des images et utiliser l'opérateur ternaire) 9


zéro post, il affiche "Aucun post pour le moment", sinon il affiche la liste des
posts.

Evaluer
Quizz

Question en gras

Réponse 1

etc

Question en gras

Correction

Recopie de la question

Correction (indiquer par ✅ ou ❌ la bonne ou mauvaise réponse)

Pour aller plus loin

Implémenter un "empty state" (+ importer des images et utiliser l'opérateur ternaire) 10


Ajouter un post : Créer le
composant

Objectifs

Savoir créer un composant vide qui va nous permettre de créer un nouveau


post.

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.

Pour le moment, ce nouveau composant CreatePost doit simplement afficher


"Créer un post".

Consigne n°1 :

Ajouter un post : Créer le composant 1


La hiérarchie des composants (visible depuis les "React Dev Tools") devra être
la suivante :

Consigne n°2 :

L'affichage devra ressembler à ceci :

Ajouter un post : Créer le composant 2


Correction

Créer un fichier CreatePost.js dans le répertoire components avec le contenu


suivant :

function CreatePost() {
return <div>Nouveau post</div>
}

export default CreatePost;

Ajouter un post : Créer le composant 3


Dans App.js , ajouter import CreatePost from './CreatePost' et ajouter <CreatePost />

entre Header et Feed, ce qui nous donne :

import Header from './Header'


import Feed from './Feed'
import CreatePost from './CreatePost'

function App() {
return (
<>
<Header />
<CreatePost />
<Feed />
</>
);
}
export default App;

Résultat :

Ajouter un post : Créer le composant 4


Ajouter un post : Créer le composant 5
Ajouter un post : Champs
contrôlés VS non contrôlés

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

On a vu qu'avec React, l'affichage est comme synchronisé avec le state : si j'ai


"Vincent" dans mon state, j'ai "Vincent" à l'écran, si je change la valeur du state
avec "Quentin", j'ai "Quentin" à l'écran, mis à jour en temps réel.

Du coup, cette logique peut poser un problème pour les champs de formulaire :

Imaginons un champ texte pré-rempli avec le prénom de l'utilisateur :

J'aurai probablement dans mon composant une variable d'état prenom qui vaut
"Vincent".
Et mon champ texte utilisera cette valeur, par exemple :

<input type="text" value={prenom}>

Ajouter un post : Champs contrôlés VS non contrôlés 1


Si je veux changer le prénom, je vais taper sur la touche retour arrière de mon
clavier pour effacer les lettres de droite à gauche (effacer le t, puis le n, puis le
e, etc...).

Que se passe-t-il lorsque j'efface la première lettre (le "t") ?


On pourrait penser que l'input va afficher "Vincen". Mais non ! Puisque
l'affichage est comme synchronisé avec mes variables d'état et que ma
variable d'état vaut "Vincent", rien ne se passera, le "t" ne va pas vouloir se
supprimer, j'ai en fait un champ qui est condamné à toujours garder la même
valeur : un champ read-only.

D'ailleurs, la console JavaScript nous en informe :

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

Ajouter un post : Champs contrôlés VS non contrôlés 2


Reproduire le problème

Commençons par reproduire le problème que nous venons de décrire, avec le


champ texte qui est bloqué avec une valeur fixe.
Pour cela, il nous suffit de créer un state postText (ayant pour valeur initiale
une chaîne de caractères vide, car au chargement de la page on veut que le
champ soit vide) qui va être utilisé comme attribut value de notre input :

import { useState } from "react"

function CreatePost() {

const [postText, setPostText] = useState('')

return <>
<div>Nouveau post</div>
<input type="text" value={postText}></input>
</>
}

export default CreatePost;

Essayez de taper du texte dans ce champ : impossible !


Le state a comme valeur une chaîne de caractères vide donc notre champ de
texte va rester synchronisé avec cette valeur et restera vide.

La méthode de l'input contrôlé par le state (recommandée)

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.

Ajouter un post : Champs contrôlés VS non contrôlés 3


Comme vu lors du chapitre sur les événements, lors d'un événement nous
avons accès à une variable event qui nous permet d'accéder à l'élément HTML
à l'origine de l'événement grâce à event.target .

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é :

import { useState } from "react"

function CreatePost() {

const [postText, setPostText] = useState('')

const onChangeHandler = (event) => {


setPostText(event.target.value)
}

return <>
<div>Nouveau post</div>
<input type="text" value={postText} onChange={onChangeHandler}></input>
</>
}

export default CreatePost;

On a maintenant un champ texte modifiable, avec une varible postText qui


reflète à chaque instant la saisie de l'utilisateur.

La méthode de l'input non contrôlé par le state (à éviter)

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.

Ajouter un post : Champs contrôlés VS non contrôlés 4


Le problème de cette méthode est qu'au moment où nous voudrons lire la
valeur du champ (par exemple lorsque l'utilisateur va appuyer sur un bouton
pour valider), nous aurons accès à un événement click provenant du bouton
ou submit provenant du formulaire, mais nous n'aurons pas à cet instant là un
événement provenant du champ texte, nous ne pourrons donc pas faire
event.target.value pour en lire la valeur.

Comment lire la valeur d'un élément du DOM avec React ?


Il ne faut surtout pas essayer d'accéder à l'élément via un sélecteur comme un
id positionné sur le champ, car on va entrer dans des conflits entre le DOM du
navigateur et le Virtual DOM de React. Toute manipulation du DOM en direct
doit être évitée lorsqu'on travaille avec React.

Il faut passer par le mécanisme de référence React.

Grâce à useRef, nous pouvons créer une référence (toujours passer null à
useRef pour ce use case précis) :

const inputRef = useRef(null)

Que nous pouvons ensuite lier à n'importe quel élément HTML, via l'attribut ref,
comme ceci :

<input type="text" ref={inputRef}></input>

Grâce à cette référence, on pourra faire à tout instant un inputRef.current.value

pour accéder à la valeur actuelle du champ texte.

Nous pouvons le tester en créant un bouton et en récupérant la valeur du


champ texte comme ceci :

import { useRef } from "react"

function CreatePost() {

Ajouter un post : Champs contrôlés VS non contrôlés 5


const inputRef = useRef(null)

const handleSubmit = (event) => {


console.log("Le champ texte contient : " + inputRef.current.value);
}

return <>
<div>Nouveau post</div>
<input type="text" ref={inputRef}></input>
<input type="submit" value="Publier" onClick={handleSubmit} />
</>
}

export default CreatePost;

La méthode des références n'est pas recommandée lorsqu'il s'agit de tracker


une valeur, par contre il existe de nombreux cas où nous n'avons pas d'autre
choix que de recourir à une référence, notamment les cas où l'on veut accéder
à des méthodes natives du DOM.

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 :

import { useRef } from "react"

function CreatePost() {

const inputRef = useRef(null)

const handleClick = (event) => {


inputRef.current.focus();
}

return <>
<div>Nouveau post</div>
<input type="text" ref={inputRef}></input>
<button onClick={handleClick}>Donner le focus au champ texte</button>
</>
}

export default CreatePost;

Ajouter un post : Champs contrôlés VS non contrôlés 6


A noter

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.

Ajouter un bouton pour valider le post

Revenons maintenant à l'état précédent (input contrôlé, qui est la


recommandation officielle), afin de continuer notre implémentation du
composant CreatePost.

On va ajouter un bouton qui va afficher dans la console le texte du post à créer


:

import { useState } from "react"

function CreatePost() {

const [postText, setPostText] = useState('')

const onChangeHandler = (event) => {


setPostText(event.target.value)
}

const createPostHandler = (event) => {


console.log("Création d'un nouveau post avec le texte : " + postText)
}

return <>
<div>Nouveau post</div>
<input type="text" value={postText} onChange={onChangeHandler}></input>
<button onClick={createPostHandler}>Publier</button>
</>
}

export default CreatePost;

Ajouter un post : Champs contrôlés VS non contrôlés 7


Ajouter ce post dans le state global qui contient tous les posts

Pour que ce post s'affiche à l'écran, il suffit de l'ajouter au state posts ( qui se
trouve dans Feed ).

Lorsque nous avons voulu donner au composant Post la possibilité de


supprimer un post, nous avons créé une fonction deletePost dans le composant
Feed , et nous avons passé cette fonction au composant Post via les props.

Suivons exactement la même logique : pour donner au composant CreatePost la


possibilité de créer un post, nous allons créer une fonction createPost dans le
composant Feed , et nous allons passer cette fonction au composant CreatePost

via les props.

Dans Feed, nous allons donc ajouter la fonction qui permet d'ajouter un
nouveau post dans le state :

const addPost = (post) => {


setPosts([...posts, post])
}

Il ne nous reste plus qu'à passer cette fonction à CreatePost .

Nous avons un problème : contrairement à Post à qui on avait passé la fonction


deletePost , CreatePost n'est pas un fils du composant Feed .

Ajouter un post : Champs contrôlés VS non contrôlés 8


Concernant les props, on a dit que les données vont toujours du parent vers
l'enfant (data down, events up).
Comment passer des props à un composant qui n'est pas son enfant ?

La réponse est simple : on ne peut pas.


Nous allons avoir recours au "state lifting" (faire remonter le state) pour arriver
à nos fins.

Evaluer
Quizz

Quelle pratique est la plus recommandée ?

Les champs contrôlés par le state

Les champs non contrôlés par le state

Ajouter un post : Champs contrôlés VS non contrôlés 9


Correction

Quelle pratique est la plus recommandée ?


✅ Les champs contrôlés par le state
❌ Les champs non contrôlés par le state

Ajouter un post : Champs contrôlés VS non contrôlés 10


Ajouter un post : State Lifting

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

Puisque les props ne peuvent transiter que de composant parent vers


composant enfant, nous allons avoir un problème 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.

Imaginons une application React construite avec l'imbrication de composants


suivante :

Ajouter un post : State Lifting 1


Dans cet exemple

✅ App peut passer des props à A, B ou C


✅ A peut passer des props à B ou C
Mais...

❌ B ne pourra jamais passer de props à C, car il ne sont pas en filiation


directe.

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.

C'est ce procédé qui va permettre à notre composant CreatePost d'accéder à


notre fonction permettant de modifier le state pour y ajouter un nouveau post.

Appliquer

Dans notre cas, la hiérarchie de composants est la suivante :

Ajouter un post : State Lifting 2


La fonction addPost est forcément définie dans Feed car c'est ici qu'est le state
qui contient les posts.
Le composant qui a besoin d'accéder à addPost est CreatePost , afin d'ajouter le
nouveau post.
Feed et CreatePost sont frères dans la hiérarchie des composants, nous allons
donc devoir appliquer le state lifting.

Le state va donc remonter dans App, qui est l'ancêtre commun de ces 2
composants.

On va donc avoir :

Ajouter un post : State Lifting 3


Avec cette nouvelle organisation, on n'aura plus aucun problème pour passer
addPost de App vers CreatePost.

Réorganisation du code

Dans Feed.js, on enlève :

Le state posts (car le but de l'opération est de le faire remonter dans App)

La fonction addPost (car le but de l'opération est de le faire remonter dans


App)

Mais aussi la fonction deletePost car les fonctions utilitaires de modification


de state doivent toujours être au même endroit que le state. Si ce n'était
pas le cas, si le state posts était dans App et que deletePost restait dans
Feed , on ne pourrait pas faire setPosts dans la fonction deletePost car

setPosts a déménagé dans App .

Et on met tout ça dans App.

Ajouter un post : State Lifting 4


En l'état, le composant Feed ne pourra plus fonctionner, car il a besoin de la
variable posts et de la variable deletePost dans le return.

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 :

import Post from './Post'


import emptyImage from '../images/empty.png'

function Feed({ posts, deletePost }) {

return posts.length > 0 ?


(
<>
<h3>Fil d'actualité :</h3>
{posts.map(p => <Post key={p.id} postData={p} deletePost={deletePost} />)}
</>
) : (
<div align="center">
<img width="300" src={emptyImage} />
<h3>Aucun post pour le moment</h3>
</div>
)

export default Feed

App.js :

import { useState } from 'react'


import Header from './Header'
import Feed from './Feed'
import CreatePost from './CreatePost'

Ajouter un post : State Lifting 5


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 App() {

const [posts, setPosts] = useState(initialPosts)

const deletePost = (id) => {


setPosts(posts.filter(p => p.id != id))
}

const addPost = (post) => {


setPosts([...posts, post])
}

return (
<>
<Header />
<CreatePost addPost={addPost} />
<Feed posts={posts} deletePost={deletePost} />
</>
);
}
export default App;

Ajout du nouveau post au state

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

On va tout d'abord extraire addPost des props :

function CreatePost({ addPost }) {

J'en profite pour rappeler que faire function CreatePost({ addPost }) et utiliser
addPost est strictement équivalent à faire function CreatePost(props) et utiliser

props.addPost .

Ajouter un post : State Lifting 6


Enfin, dans notre fonction createPostHandler, il ne nous reste plus qu'à créer
un objet de type post (identique à la structure d'un post du state) et appeler
addPost :

const createPostHandler = (event) => {


const newPost = {
id: Math.floor(1000 * Math.random()), //entier aléatoire entre 0 et 1000
text: postText,
author: "Nouvel utilisateur", //pour le moment nous n'avons pas de nom
likes: 0
}
addPost(newPost)
}

On peut maintenant créer des posts !

Evaluer
Quizz

Question en gras

Réponse 1

etc

Question en gras

Correction

Recopie de la question

Correction (indiquer par ✅ ou ❌ la bonne ou mauvaise réponse)

Ajouter un post : State Lifting 7


Ajouter un post : Finalisation

Objectifs

Nous allons réaliser deux petites améliorations à la création d'un post.

Appliquer

Amélioration 1 : Conception de addPost

Il n'est pas très propre de déléguer la construction de l'objet post au


composant CreatePost. La logique de création du post (mettre un id, initialiser
les likes à zéro, etc) devrait appartenir au composant qui gère le state des
posts, afin d'avoir à un seul et même endroit la logique métier de toutes les
opérations. Il serait donc mieux d'avoir cette logique dans le composant App.

La fonction addPost ne devrait donc pas prendre en paramètre un objet post,


mais plutôt simplement le texte du nouveau post, et c'est App qui va gérer les
manipulations pour créer l'objet dans le bon format.

Côté CreatePost, on va donc simplifier le createPostHandler en faisant


simplement :

const createPostHandler = (event) => {


addPost(postText)
}

Et dans App, on va créer l'objet en utilisant ce postText passé en paramètre :

Ajouter un post : Finalisation 1


const addPost = (postText) => {
const newPost = {
id: Math.floor(1000 * Math.random()), //entier aléatoire entre 0 et 1000
text: postText,
author: "Nouvel utilisateur",
likes: 0
}
setPosts([...posts, newPost])
}

Evaluer

Amélioration 2 : Réinitialisation du champ texte

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

Amélioration 2 : Réinitialisation du champ texte

Il suffit de réinitialiser le state postText à une valeur vide juste après avoir créé
le post :

const createPostHandler = (event) => {


addPost(postText)
setPostText('')
}

Ajouter un post : Finalisation 2


Ajouter un post : Finalisation 3
Le pattern &&

Objectifs

Réaliser trois petites améliorations à la création d'un post.

Comprendre

En JavaScript, lorsqu'on fait a && b , contrairement à d'autres langages, le


résultat n'est pas forcément true ou false.

L'implémentation de a && b en JavaScript est la suivante :

Si a est faux, le résultat est false

Si a est vrai, le résultat est b

Du coup, on peux utiliser cette spécificité pour créer une sorte de if

compressé.

Par exemple, en Javascript, le code suivant :

showAlert && alert("Attention")

va afficher une alerte "Attention" seulement si showAlert est true, sinon il ne se


passera rien (l'instruction retournera false dans le vide).

On peut faire le lien avec l'opérateur ternaire vu précédemment qui est un


if/else compressé:

Le pattern && 1
posts.length > 0 ? contentPosts : contentEmpty

Dans le cas où on n'a pas de else, au lieu de faire :

posts.length > 0 ? contentPosts : null

On va plutôt faire :

posts.length > 0 && contentPosts

Appliquer

Afin d'empêcher l'utilisateur de créer un post vide, une application standard


implémenterait un mécanisme de vérification côté JavaScript (par exemple
dans addPost) et côté backend avant l'enregistrement en base de données.
Ici, puisque le but est d'apprendre React, nous allons utiliser une autre
approche, plus ludique, qui est de masquer le bouton Publier tant que le champ
texte est vide.

Nous allons voir ensemble comment le faire avec un if classique, puis avec
l'opérateur &&.

Pour le faire avec un if classique, on va faire :

function CreatePost({ addPost }) {

// code de useState, onChangeHandler et createPostHandler déjà vu

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

Voici comment faire la même chose avec && :

function CreatePost({ addPost }) {

// code de useState, onChangeHandler et createPostHandler déjà vu

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.

Note : si on veut également interdire la saisie d'un post contenant uniquement


des espaces, on peut remplacer postText != '' par postText.trim() != '' (mais là
on est sur du JavaScript, rien de spécifique à React).

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é :

Pouvoir ajouter une image à un post

Afficher une photo de profil sur chaque utilisateur

Afficher l'heure à laquelle a été publié le post

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

Posts avec images, photos de profils et heure du post 1


. Assurez-vous bien d'être dans le répertoire du projet React (le
install moment

répertoire qui contient le ficher package.json ) avant de lancer la commande.

Le reste n'est que de la gymnastique HTML et React identique à ce qui a déjà


été vu jusqu'ici, rien de nouveau ici donc.

Posts avec images, photos de profils et heure du post 2


Styler nos composants (CSS)

Objectifs

Nous allons enfin rendre notre application un peu plus jolie en y ajoutant des
styles CSS.

Comprendre

Ajouter du CSS à des composants React présente certaines spécificités.

Voyons tout cela en détail.

2 particularités de syntaxe :

Le mot clé class (⇒ className)

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.

Styler nos composants CSS 1


Une autre conséquence de cela est que nous allons devoir accéder aux
propriétés CSS via la syntaxe 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.

Les différentes méthodes

Il existe plusieurs manières d'ajouter du style à nos composants :

Style inline

✅ Le plus rapide, pas besoin de donner des noms de classes et de


naviguer entre les fichiers

❌ Cela alourdit le code de notre composant


Fichier CSS global

✅ On écrit avec la syntaxe CSS classique


❌ Le fichier CSS va devenir énorme

Styler nos composants CSS 2


❌ Les styles sont globaux, donc peuvent rentrer en conflit s'ils sont
référencés dans plusieurs composants

❌ On perd la modularité des composants : par exemple, si vous voulez


extraire un composant pour réutiliser dans un autre projet, vous allez
devoir démêler quelles lignes de votre CSS sont utiles à votre
composant.

Modules CSS

✅ Syntaxe CSS classique


✅ Code CSS indépendant entre chaque module
❌ Devoir importer et utiliser une variable dans chaque fichier n'est pas
la chose la plus simple et rapide

styled-components

✅ Syntaxe CSS classique


❌ Alourdit le code des composants
❌ Nécessite d'installer un module (styled-components)
SASS

✅ Syntaxe CSS classique


✅ Code CSS indépendant entre chaque module
❌ Nécessite d'installer un module (node-sass)

Appliquer

Styler nos composants CSS 3


Dans cette partie, nous allons manipuler chacune des méthodes mentionnées
plus tôt pour ajouter des styles CSS à notre application.

Pour aller plus loin

Dans cette partie, j'implémente la partie CSS de notre application.

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.

Styler nos composants CSS 4


Le hook useEffect

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

React fonctionne avec de la programmation déclarative. Ce type de programmation ne


permet pas de décrire des scénarios du type aller récupérer des données via un appel à
un backend. Pour faire ce genre de choses, nous sommes obligés de passer par de la
programmation impérative en contraignant une fonction à s'exécuter uniquement à un
ou certains moments particuliers du cycle de vie de notre composant.

Le hook useEffect va nous permettre de faire cela.

Voyons les différents cas d'utilisation :

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() {

const [prenom, setPrenom] = useState("Vincent")


const [nom, setNom] = useState("Andrieu")
const [ville, setVille] = useState("Toulouse")

console.log("Bonjour " + nom)

return <h1>Mon application</h1>

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

1er cas d'utilisation de useEffect :

Nous pouvons déclencher ce même code de la manière suivante :

function App() {

useEffect(() => {
console.log("Bonjour " + nom)
})

return <h1>Mon application</h1>

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.

C'est un cas d'utilisation assez marginal.

Cas d'utilisation suivant :


Lancer notre fonction uniquement si nom change de valeur, grâce au 2eme argument de
useEffect, qui est une liste des variables que React doit "observer" pour déclencher le
useEffect à chaque modification :

function App() {

const [prenom, setPrenom] = useState("Vincent")


const [nom, setNom] = useState("Andrieu")
const [ville, setVille] = useState("Toulouse")

useEffect(() => {
console.log("Bonjour " + nom)
}, [nom])

return <h1>Mon application</h1>

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() {

const [prenom, setPrenom] = useState("Vincent")


const [nom, setNom] = useState("Andrieu")
const [ville, setVille] = useState("Toulouse")

useEffect(() => {
console.log("Bonjour " + prenom + " " + nom)
}, [prenom, nom])

return <h1>Mon application</h1>

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() {

const [prenom, setPrenom] = useState("Vincent")


const [nom, setNom] = useState("Andrieu")
const [ville, setVille] = useState("Toulouse")

useEffect(() => {
console.log("Bonjour " + nom)
}, [])

return <h1>Mon application</h1>

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() {

const [prenom, setPrenom] = useState("Vincent")


const [nom, setNom] = useState("Andrieu")
const [ville, setVille] = useState("Toulouse")

useEffect(() => {
console.log("Bonjour " + nom)

Le hook useEffect 3
return () => console.log("Au revoir " + nom);
}, [])

return <h1>Mon application</h1>

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 :

const bouchonBackend = () => {


return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(initialPosts)
}, 2000);
})
}

La valeur initiale de notre state posts ne sera plus la constante initialPosts mais un
tableau vide :

const [posts, setPosts] = useState([])

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)
})
}, [])

Evidemment, il ne faut pas oublier d'importer ce hook en haut du fichier :

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 :

const [isLoading, setIsLoading] = useState(true)

useEffect(() => {
bouchonBackend().then(posts => {
setPosts(posts)
setIsLoading(false)
})
})

<Feed posts={posts} deletePost={deletePost} likePost={likePost} isLoading={isLoading} />

Dans Feed.js :

//Si on est en train de charger


if (isLoading) return <div>Chargement...</div>

//Si on n'a aucun post on affiche l'écran "empty state"


if (posts.length == 0) return (
<>
<h3 align="center">
<EmptyImageSvg width="300" height="300" />
<div>
Aucun post pour le moment
</div>
</h3>
</>
)

//Affichage des posts


return posts.map(p => <Post key={p.id} postData={p} deletePost={deletePost} likePost={likePost} />)

Evaluer

Le hook useEffect 5
Quizz

Lesquelles de ces choses peut-on faire avec useEffect ?

Exécuter un morceau de code seulement quand un state particulier change

Exécuter un morceau de code uniquement au chargement (montage) du composant

Exécuter un morceau de code uniquement au déchargement (démontage) du


composant

Correction

Lesquelles de ces choses peut-on faire avec useEffect ?

✅ Exécuter un morceau de code seulement quand un state particulier change


✅ Exécuter un morceau de code uniquement au chargement (montage) du composant
✅ Exécuter un morceau de code uniquement au déchargement (démontage) du
composant

Toutes les réponses sont justes !

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

Code plus verbeux, plus difficile à maintenir

On ne peut pas utiliser les hooks

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 ?

Voici le code de notre composant fonctionnel CreatePost :

import { useState, useEffect } from 'react'

function CreatePost({ addPost }) {

const [postText, setPostText] = useState('')


const [postPicture, setPostPicture] = useState('')

function onPostTextChangeHandler(event) {
setPostText(event.target.value)
}
const onPostPictureChangeHandler = (event) => {
setPostPicture(event.target.value)
}

const createPostHandler = (event) => {

Les composants basés sur des classes 1


addPost(postText, postPicture)
setPostText('')
setPostPicture('')
}

useEffect(() => {
console.log("Chargement du composant fonctionnel");
return () => console.log("Démontage du composant fonctionnel");
}, [])

const postHasText = postText.trim() !== ''

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>
)
}

export default CreatePost

Transformons le en composant classe.

Voici le résultat :

import React from 'react'

class CreatePost extends React.Component {

constructor(props) {
super(props)
this.state = {
postText: '',
postPicture: '',
}
}

componentDidMount() {
console.log("Chargement du composant classe");
}

componentWillUnmount() {
console.log("Démontage du composant classe");
}

onPostTextChangeHandler = (event) => {


this.setState({ postText: event.target.value })
}
onPostPictureChangeHandler = (event) => {
this.setState({ postPicture: event.target.value })
}

createPostHandler = (event) => {


this.props.addPost(this.state.postText, this.state.postPicture)
this.setState({
postText: event.target.value,
postPicture: event.target.value
})
}

render() {

Les composants basés sur des classes 2


const postHasText = this.state.postText.trim() !== ''

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>
)

export default CreatePost

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.

Les composants basés sur des classes 3


Les hooks

Objectifs

Comprendre ce que sont les hooks et en quoi ils sont utiles.

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.

Les hooks fournis par React

Les 2 plus importants sont de loin useState et useEffect, qui permettent


respectivement de gérer l'état d'un composant, et de créer des effets de bord.

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"

Les Hooks de base

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 nous allons voir dans la suite de la formation useContext, useMemo et


useCallback, qui sont moins en marge que les autres.

Les custom hooks

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.

Voyons en détail les custom hooks dans le chapitre suivant.

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.

On pourrait par exemple créer un custom hook pour :

Observer une variable et loggue sa nouvelle valeur dès qu’elle change

Persister un state dans le local storage

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 :

import { useEffect } from 'react'

const useLogVariable = (variable) => {

useEffect(() => {
console.log("Nouvelle valeur :", variable);
}, [variable])

}
export default useLogVariable

Et on l'utiliserait ainsi :

useLogVariable(postText)

Encore plus intéressant serait un custom hook qui permettrait de synchroniser


une variable de state avec le local storage, afin de créer une persistance des
données :

Custom hooks 2
import { useState, useEffect } from 'react'

const getValueFromStorage = key => JSON.parse(localStorage.getItem(key))


const setValueInStorage = (key, value) => localStorage.setItem(key, JSON.stringify(value))

const useLocalStorage = (initialValue, storageKey) => {

const [data, setData] = useState(getValueFromStorage(storageKey) || initialValue)

useEffect(() => {
setValueInStorage(storageKey, data)
}, [data])

return [data, setData]

}
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 :

const [posts, setPosts] = useLocalStorage(initialPosts, "react_network_posts")

Les custom hooks nous permettent donc de créer des fonctionnalités


réutilisables intégrant des variables d'état et des effets de bord.

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.

Par exemple, la page d'accueil va charger index.html , la page "à propos" va


charger about.html , etc...

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.

En React, nous n'aurons pas ça puisque tout est fait dynamiquement en


Javascript.

Le fonctionnement en React est le suivant : grâce à une librairie npm qui


s'appelle react-router , nous allons pouvoir depuis notre code React, charger tel
ou tel composant en fonction de l'URL tapée dans la barre d'adresse du
visiteur.

Appliquer

Création de nos composants/pages

Ajouter d'autres pages avec react router 1


Avant d'utiliser react-router, nous allons créer 3 composants qui seront les
différentes pages de notre application : HomePage , Profile et About .

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

Nous allons installer la librairie react-router via la commande suivante :


npm install react-router-dom

La dépendance est ajoutée dans package.json .

Mise en place

Pour utiliser react-router, nous allons placer un BrowserRouter autour de notre


application, au plus haut niveau, dans index.js :

import { BrowserRouter } from 'react-router-dom'

Ajouter d'autres pages avec react router 2


ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);

Maintenant, nous pouvons utiliser le composant <Route path="..."> qui permet


de n'afficher son contenu que si l'url matche l'attribut path :

import { Route, Switch, Redirect } from 'react-router-dom'

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

 Le path est permissif, c'est-à-dire que s'il correspond en partie, c'est


considéré comme un match. Du coup, si je vais sur la page /about , <Route
path="/"> matche car /about contient / .

Ajouter d'autres pages avec react router 3


Nous pouvons éliminer ce problème en résolvant le point numéro 1 via le
composant <Switch> . Grâce au composant Switch, dès qu'on matche une route,
on ignore les suivantes :

<Switch>
<Route path="/">
<HomePage />
</Route>
<Route path="/about">
<AboutPage />
</Route>
<Route path="/profile">
<ProfilePage />
</Route>
</Switch>

Effectivement, sur la page d'accueil, on a bien uniquement la page d'accueil.


Mais, sur la page /about, au lieu d'avoir "à propos", nous avons également la
page d'accueil !

Ceci est dû au match permissif dont on a parlé plus haut. /about matche /

donc on affiche la HomePage, et vu qu'il y a le Switch on s'arrête là.

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>

À présent, toutes nos routes sont ok !

Erreurs 404 ou redirections

Ajouter d'autres pages avec react router 4


Si l'utilisateur de notre site va sur une URL qui n'existe pas, on peut soit le
rediriger vers une page qui affiche une erreur 404 (le statut HTTP ne sera pas
404, mais ce n'est pas très grave car nous sommes sur un frontend et pas sur
une API, ou bien le rediriger vers la page d'accueil.

On va faire ça grâce à une route qui matche toujours ( path="*" ).

Pour avoir une erreur 404, on va faire :

<Route path="*">
<h3>Erreur 404 - Not found</h3>
</Route>

Et pour rediriger vers la page d'accueil, on va importer Redirect du package


react-router-dom :

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

Ajouter d'autres pages avec react router 5


Grâce à react-router, on va pouvoir changer ce mécanisme pour avoir une
navigation instantanée, 100% Javascript, qui permet d'éviter au navigateur de
faire une requête HTTP pour récupérer le contenu et qui évite donc un
rechargement de page.

Pour cela, on va utiliser le composant Link fourni par react-router :

import { Link } from 'react-router-dom'

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>
)
}

export default Header

Lorsqu'on clique sur un menu, on a maintenant une navigation instantanée,


sans rechargement de page, on se croirait dans une application mobile,
l'expérience utilisateur est vraiment agréable.

Ajouter d'autres pages avec react router 6


Passage de paramètres dans
une URL

Objectifs

Ce chapitre va nous permettre d'apprendre à passer des paramètres dans une


URL

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 :

import { useParams } from 'react-router-dom'

function ProfilePage() {
return (
<div>
<h1>Page Mon profil</h1>
<h3>Ceci est le profil numéro {useParams().id}</h3>
</div>

Passage de paramètres dans une URL 1


)
}

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 .

Passage de paramètres dans une URL 2


Mise en place d'une layout
commune entre les différentes
pages

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"> :

Mise en place d'une layout commune entre les différentes pages 1


function MainLayout(props) {
return (
<>
<Header />
<div className="center500px">

</div>
</>
)
}

Et nous ne laissons dans HomePage.js que les parties qui concernent


uniquement la HomePage :

<CreatePost addPost={addPost} />


<Feed posts={posts} deletePost={deletePost} likePost={likePost} isLoading={isLoading} />

Maintenant, nous allons injecter le contenu de la HomePage à l'intérieur du div


center500px de notre layout.

Pour cela, 2 étapes :

Premièrement, passer le contenu de la HomePage à l'intérieur de la balise


MainLayout :

<MainLayout>
<CreatePost addPost={addPost} />
<Feed posts={posts} deletePost={deletePost} likePost={likePost} isLoading={isLoading} />
</MainLayout>

Deuxièmement, dans le composant MainLayout, récupérer le contenu qui nous


est passé (grâce à props.children) et l'afficher dans le div center500px :

function MainLayout(props) {
return (
<>
<Header />
<div className="center500px">
{props.children}

Mise en place d'une layout commune entre les différentes pages 2


</div>
</>
)
}

Nous pouvons améliorer ce code :

Plutôt que d'ajouter une dépendance à MainLayout dans notre code de


HomePage, nous pouvons intégrer la totalité de notre application dans notre
Layout.

Pour cela, il nous suffit d'aller dans App.js et de mettre notre layout autour du
switch :

import AboutPage from "../pages/AboutPage";


import HomePage from "../pages/HomePage";
import ProfilePage from "../pages/ProfilePage";
import { Route, Switch, Redirect } from 'react-router-dom'
import MainLayout from "../layouts/MainLayout";

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;

Mise en place d'une layout commune entre les différentes pages 3


Avec un peu de CSS et un petit state (rien de nouveau, concepts déjà appris
dans les chapitres précédents), nous pouvons même ajouter une sidebar de
messagerie instantanée et grâce au fait que notre layout est commune à toutes
nos pages, nous avons maintenant 3 écrans avec une même mise en page
commune :

Mise en place d'une layout commune entre les différentes pages 4


Mise en place d'une layout commune entre les différentes pages 5
Evaluer
Quizz

Dans quel but crée-t-on des composants de type Layout ?

Créer des pages dynamiquement

Différencier la structure de nos pages

Partager une structure de page commune entre différentes pages

Mise en place d'une layout commune entre les différentes pages 6


Correction

Dans quel but crée-t-on des composants de type Layout ?


❌ Créer des pages dynamiquement
❌ Différencier la structure de nos pages
✅ Partager une structure de page commune entre différentes pages

Mise en place d'une layout commune entre les différentes pages 7


Global State Management (redux, Context
API)

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

Il existe plusieurs manières de gérer un state global.

Librairies spécialisées (non incluses dans React)

La plus connue est redux.


Nous n'allons pas rentrer dans redux car ce serait l'objet d'une formation à part entière. Cette librairie a été
créée car auparavant il n'existait aucun autre moyen de gérer un état global dans React.

Les 2 inconvénients à l'utilisation de redux sont :

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

La Context API (incluse dans React depuis React v16.3)

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

Global State Management (redux, Context API 1


Nous allons illustrer cette faculté à gérer un état global en ajoutant des zones de texte dans la page "Mon
profil" pour changer le nom et l'image de l'utilisateur connecté, et récupérer et utiliser ces informations
depuis d'autres composants comme CreatePost ou AboutPage, sans aucune transmission de props.

La première étape est de créer un contexte :

UserContext.js:

import { createContext } from 'react'

const UserContext = createContext(null)

export default UserContext

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:

import UserContext from '../contexts/UserContext'

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() {

const [user, setUser] = useState({ author: "John Doe", authorPicture: "https://picsum.photos/seed/profile4545/50/50" })

return (
<UserContext.Provider value={{ user }}>
...

Ça y est, notre state user est partagé avec tous nos composants !

Voici comment y accéder :

Première étape : importer le hook useContext et notre contexte UserContext :

Global State Management (redux, Context API 2


import { useContext } from 'react'
import UserContext from '../contexts/UserContext'

Deuxième étape, récupérer le state user depuis le contexte :

const { user } = useContext(UserContext)

Ce qui nous donne le code suivant pour la page AboutPage.js :

import { useContext } from 'react'


import UserContext from '../contexts/UserContext'

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 :

<UserContext.Provider value={{ user, setUser }}>

Du coup, dans ProfilePage, on peut maintenant faire :

import { useState, useContext } from 'react'


import UserContext from '../contexts/UserContext'

function ProfilePage() {

const { user, setUser } = useContext(UserContext)

const updateProfile = e => {


e.preventDefault()
setUser({
author: userNameText,
authorPicture: profilePictureText,
})
alert("Profil mis à jour.")
}

Et notre user est bien mis à jour dans notre state global !

Global State Management (redux, Context API 3


Les hooks useMemo et
useCallback

Objectifs

Ce chapitre va nous permettre d'expérimenter 2 hooks dont le comportement


est similaire : useMemo et useCallback.

Appliquer

useMemo

Dans notre composant AboutPage , nous allons ajouter 2 states :

1 compteur (state count qui est un nombre entier)

1 switch on/off (state isOn booléen)

const [count, setCount] = useState(1)


const [isOn, setIsOn] = useState(true)

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.

const calcul = (nb) => {


console.log("start calcul");
return nb * nb
}
const result = calcul(count)

Les hooks useMemo et useCallback 1


Dans le rendu de notre composant, nous allons afficher la valeur du compteur et
du switch, mais également des boutons pour modifier les valeurs.

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

Regardons ce qui se passe si on change le compteur ;


Le calcul est bien exécuté pour calculer la nouvelle valeur.

Maintenant, regardons ce qui se passe si on change le switch :

Le calcul est également exécuté ! En effet, changer le switch implique de


changer l'état du composant, et changer l'état du composant c'est envoyer à
React le signal de le rendre à nouveau.

Dans de nombreux cas, ceci ne sera pas un problème et on gardera ce


fonctionnement.

Mais imaginons maintenant que le calcul soit une opération synchrone complexe
:

const calcul = (nb) => {


console.log("start calcul");
const max = 200000000
let sum = 0
for (let i = 1; i < max; i++) {
sum += Math.sqrt(i) * Math.tanh(i) * Math.cos(i * 7 / (i * i)) * (i % 2 == 0 ? -1 : 1)
}
return sum + nb
}

Les hooks useMemo et useCallback 2


Lorsqu'on change le compteur, l'affichage suivant prend du temps, ce qui est
compréhensible, mais lorsqu'on change le switch il n'est pas vraiment optimal de
perdre plusieurs secondes pour calculer un résultat que l'on a déjà calculé juste
avant, car le count n'a pas changé.

useMemo va nous permettre de résoudre ce problème en se remémorant la


dernière valeur calculée (comme un cache), et si les paramètres de notre calcul
ne changent pas, il va restituer la valeur précédemment calculée.

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) :

const result = useMemo(() => calcul(count), [count])

A présent, on peut changer le switch et le résultat du calcul est instantané.

useCallback

useCallback va nous aider à résoudre un problème similaire qui arrive lorsque la


fonction en question est passée à un composant enfant.

Par exemple, passons la fonction et le count à un sous composant ShowResult :

<ShowResult calcul={calcul} count={count} />

C'est maintenant ce composant ShowResult qui va se charger de lancer le calcul


et d'afficher le résultat :

Les hooks useMemo et useCallback 3


function ShowResult(props) {

const [result, setResult] = useState('')

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>
)

Le problème que nous rencontrons est le suivant : lorsqu'on modifie le switch


dans le composant parent, on voit le log resultat=... dans le sous composant.

Pourtant, ni props.count ni props.calcul n'ont changé. Et bien si ! En changeant le


switch, on a régénéré le composant parent AboutPage), on a donc recréé la
fonction calcul, mais en lui donnant une nouvelle référence en mémoire vive. Le
code de la fonction est le même, mais en terme d'objet en mémoire, c'est une
autre fonction !
Du point de vue de ShowResult, props.calcul a donc changé de valeur, on va
donc exécuter le useEffect et donc relancer la fonction inutilement.

Grâce à useCallback, on va pouvoir dire que lorsqu'on régénère la fonction


calcul , on veut garder la même référence, afin que ShowResult puisse voir que

la fonction est la même.

Pour cela, on va donc enrober la déclaration de calcul avec un useCallback :

const calcul = useCallback((nb) => {


//code de la fonction
}, [])

Les hooks useMemo et useCallback 4


Et à présent, lorsqu'on change le switch, aucun calcul n'est lancé.

Pour aller plus loin

Notons que les 2 hooks ont une signature similaire 1er argument : fonction,
2eme argument : liste des dépendances) et des fonctionnalités quasi similaires :

useMemo conserve la valeur de retour de la fonction d'un rendu à l'autre

useCallback conserve la fonction elle-même d'un rendu à l'autre

La relation intime qui existe entre les 2 est en fait la suivante :

useMemo(() ⇒ myFunction) est identique à useCallback(myFunction) .

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

Les hooks useMemo et useCallback 5


Récupérer des données backend
depuis une API

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.

Création de notre backend

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.

jsonplaceholder est un des plus connus.

Nous allons utiliser https://mocky.io/ qui permet de customiser la réponse.

Dans mocky, nous allons créer une URL qui renvoie le JSON correspondant à nos
posts.

Nous avons donc un backend avec une URL dédiée !

Récupérer des données backend depuis une API 1


Appel du backend depuis React

Tout d'abord, nous allons avoir besoin d'installer node-fetch pour utiliser le Fetch
API qui permet de faire des requêtes HTTP :

npm install node-fetch

Ensuite, nous allons faire la requête :

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 !

Récupérer des données backend depuis une API 2


Tirer profit de la communauté
et des packages npm

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.

Tirer profit de la communauté et des packages npm 1


Déployer notre appli react en
production

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

La simplicité de cette étape est parfois déroutante.

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 !

Build de notre application

Pour "builder" notre application, il suffit de lancer la commande :


npm run build

Un nouveau répertoire "build" a été créé dans votre projet : il contient le site
web optimisé et packagé.

Déployer notre appli react en production 1


Déploiement de notre application

Allez sur votre serveur et uploadez les fichiers.


Ou bien:

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 !

Voici l'URL de celui que j'ai créé :


https://happy-pike-ba8281.netlify.app/

Evaluer
Quizz

Que déploie-t-on lorsqu'on héberge un site React sur un serveur ?

Uniquement des fichiers HTML, CSS et JavaScript

Des fichiers HTML, CSS et JavaScript, mais aussi du Node.JS pour faire
tourner la partie serveur

Correction

Que déploie-t-on lorsqu'on héberge un site React sur un serveur ?


✅ Uniquement des fichiers HTML, CSS et JavaScript
❌ Des fichiers HTML, CSS et JavaScript, mais aussi du Node.JS pour faire
tourner la partie serveur

Déployer notre appli react en production 2


Conclusion
Bravo à vous, et merci de m'avoir suivi jusqu'ici !

Vous maîtrisez maintenant JavaScript et React.JS suffisamment pour pouvoir


trouver sans souci un poste de développeur front-end.

Quid de la suite ?

La prochaine étape sera de bien pratiquer React.JS. Rien ne sert d'apprendre


trop de technos en même temps sans les pratiquer. Commencez par
développer plusieurs projets en React avant de passer à autre chose.

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) :

Avec Gatsby.JS, pour un rendering statique au moment du build

Avec Next.JS, pour un rendering en temps réel par le serveur

J'espère sincèrement que le contenu vous a plu, et je vous dis peut-être à


bientôt !

Conclusion 1

Vous aimerez peut-être aussi