TP3 - React Router - Partie 1
Introduction
A mesure que la complexité d'une application augmente, on commence à vouloir séparer notre contenu en écrans, afin de pouvoir exposer davantage de fonctionnalités, et reproduire un comportement familier pour l'utilisateur : nous parlons ici de navigation.
Bien que ce soit relativement aisé à accomplir seulement avec React, il y a de nombreux bénéfices à utiliser une librairie externe pour gérer la navigation de l'utilisateur.
Dans ce TP, nous allons pratiquer React Router. React Router est un paquet de la communauté, il n'est donc pas directement maintenu par Facebook. Cependant, c'est un projet mature, et présent dans l'écosystème React depuis longtemps. Par conséquent, il dispose de nombreuses fonctionnalités, est familier pour beaucoup de développeurs React, fiable, facile à comprendre, et très bien intégré aux fonctions de navigation des navigateurs. Vous pouvez trouver sa documentation ici.
Objet du TP
Durant ce TP, vous allez créer une application (minimaliste) de critiques de vins. L'objectif est d'apprendre à se servir des fonctionnalités courantes d'un routeur front-end.
Comme d'habitude, vous pourrez trouver le résultat sur cette page.
Mise en place
Vite
Créez un nouveau projet avec Vite à l'emplacement de votre choix :
npm create vite@latest react-tp3 -- --template react
Projet Gitlab
Créez maintenant un projet react-tp3 sur le Gitlab de l'université.
Pensez bien à décocher l'option proposant d'initialiser le repository !
Copiez l'adresse de ce nouveau repository à l'aide du bouton "Cloner avec SSH", puis exécutez les commandes suivantes dans le dossier de votre application crée à l'étape précédente en remplaçant <git-repo> par cette dernière :
# On initialise le dépôt Git local
git init
# On ajoute les fichiers existants au dépôt
git add .
# On effectue notre tout premier commit
git commit -m "Initial commit"
# On ajoute le projet créé précédemment comme distante
git remote add origin <git-repo>
# On finit par pousser notre commit sur le Gitlab de l'université !
git push -u origin main
Vous devriez maintenant voir sur le README.md de votre projet sur le Gitlab de l'université la documentation générée par le kit de démarrage de Vite.
Starter code
Vous pouvez maintenant supprimer tout le contenu du dossier src, et décompresser l'archive suivante à la place.
Séparer les composants
Une fois votre projet en place et le serveur de développement lancé, vous allez pouvoir observer à quoi ressemble la base de votre application.
Séparons donc quelques composants.
Le composant Review
Comme vous pouvez le constater, le premier bloc de notre application représente une critique de vin. Extrayez le morceau de code correspondant et placez le dans un nouveau composant nommé Review, situé dans un nouveau fichier src/components/Review.jsx.
Laissez ce morceau de code tel quel pour le moment, nous y reviendrons plus tard. Vous pouvez aussi l'enlever du composant App pour le moment.
Le composant ReviewList
Si on continue de décomposer la structure de notre composant App, le prochain composant à séparer est la liste des critiques de vin.
Extrayons cette liste dans un nouveau composant ReviewList dans un fichier src/components/ReviewList.jsx.
Modifiez ensuite ce composant de manière à afficher toutes les critiques présentes dans fichier src/api/db.json.
Dans une application réelle, vous auriez probablement dû faire une requête AJAX pour aller chercher la liste des critiques, mais dans un souci de simplicité, vous pouvez importer ce fichier directement dans votre code.
Layout
Le reste de notre composant App consiste maintenant en une barre de menu en haut de page ainsi qu'un footer en bas de page.
Ces éléments pourraient rester tels quels dans le composant App, mais afin de le rendre plus lisible, nous allons les extraire dans un nouveau composant nommé Layout, que vous placerez dans un fichier src/components/Layout.jsx.
On veut extraire ces composants, mais laisser le composant ReviewList dans le composant App. Pour ce faire, notre composant Layout doit pouvoir être utilisé de cette façon :
<Layout>
<ReviewList />
</Layout>
Une prop spéciale nommée children est passée au composant Layout quand il est utilisé de cette manière... Servez vous en pour afficher ses éléments enfants...
Une fois ceci terminé, faites un commit avec le commentaire "Ex 1 : Splitting components" et poussez vers le serveur.
git add .
git commit -m 'Ex 1 : Splitting components'
git push
Afficher une critique
Entrons dans le vif du sujet : nous souhaitons avoir plusieurs pages au sein de notre application, et notamment une page permettant d'afficher une seule et unique critique de vin.
Commençons par installer React Router :
npm install react-router-dom
Nous installons le paquet react-router-dom et non react-router, qui contient en plus de la base de React Router les utilitaires pour manipuler la navigation dans un navigateur web. Sachez qu'il existe aussi un paquet équivalent pour React Native (react-router-native).
L'API HTML5 History
Avec HTML5 a été introduite l'API History : cette dernière permet d'interagir avec l'historique de navigation de l'utilisateur depuis Javascript.
Elle permet notamment de manipuler l'URL dans la barre d'adresse du navigateur, donnant à l'utilisateur la possibilité d'interagir avec comme il en a l'habitude : ajout de favoris, envoi d'URL etc, même dans le cadre d'une "single page app", comme celle que nous sommes en train de construire, pour laquelle le serveur (ici le serveur de développement) ne renvoie qu'un seul et même document.
C'est aussi grâce à l'utilisation de cette API par React Router que votre navigateur ne rafraîchira pas la page quand vous cliquerez sur un lien, en ajoutant une entrée à l'historique, ce qui a pour effet de modifier l'URL courante.
L'historique de navigation consiste en une suite d'URL :
http://my-awesome-app.com/
http://my-awesome-app.com/login
http://my-awesome-app.com/dashboard
http://my-awesome-app.com/user/5
- Si l'utilisateur clique sur le bouton "Précédent" de son navigateur à l'adresse
/user/5, il reviendra à l'adresse/dashboard. - Si à partir de cette adresse, il clique sur le bouton "Suivant", il reviendra à son point de départ.
- Cependant, si il clique sur un autre lien, le reste de l'historique sera remplacé.
Vous connaissez très certainement ces principes simples, mais c'est exactement ce à quoi donne accès cette API.
React Router propose une surcouche sur cette API, permettant son bon fonctionnement dans différents environnements.
Choisir son routeur
Le paquet NPM que vous avez installé à l'instant contient différents types de routeurs dont les cas d'utilisation diffèrent. En voici les plus communs :
HashRouter
Ce type de routeur utilise window.location.hash, c'est à dire le même type de lien qu'utilisent les ancres HTML. Ainsi l'adresse d'une page "Home" pourrait être : https://monsite.com#home.
Les adresses de ce type sont moins élégantes, et ce routeur ne supporte pas certaines fonctionnalités avancées de React Router, cependant il est très largement supporté par les navigateurs, même anciens, et fonctionne dans certains cas particuliers (comme dans la correction par exemple !)
MemoryRouter
Ce routeur stocke et gère la navigation de l'utilisateur exclusivement en mémoire, et ne modifie donc pas l'URL. Il peut être utile lors de tests, ou encore dans un environnement externe à un navigateur (Electron...).
BrowserRouter
C'est le routeur que nous allons utiliser. Ce routeur est basé sur l'API history HTML5 et permet de modifier l'adresse affichée dans la barre de votre navigateur, ainsi que l'historique des adresses parcourues. Pour une application web, ce type de routeur est idéal, tant que vous n’avez pas à supporter des navigateurs plus anciens ou exotiques, ou autre besoin très spécifique.
Le routeur doit être placé plus haut dans la hiérarchie des éléments que ceux qui utiliseront les fonctionnalités de navigation. Dans notre cas, placez cet élément au plus haut niveau de notre application :
const App = () => {
return <BrowserRouter>
{/* ... */}
</BrowserRouter>
}
Ajouter des routes
React Router expose deux composants Routes et Route qui servent à déclarer les routes possibles pour notre application, ainsi que l'élément à afficher quand l'utilisateur arrive sur cette route.
Voici un exemple :
<Routes>
<Route path="hello" element={<Hello />} />
<Route path="world" element={<World />} />
<Routes>
Dans cet exemple, quand l'utilisateur arrivera à l'adresse /hello de l'application, il se verra afficher le composant Hello à l'endroit ou se trouve cet élément Route.
Les éléments Route doivent toujours être les enfants d'un élément Routes.
Essayez d'afficher le composant Review quand l'utilisateur arrive sur le chemin /view. Testez en entrant l'adresse dans la barre d'adresse de votre navigateur.
Modifiez ensuite le lien "Random" du menu pour pointer sur cette page. Que remarquez vous lorsque vous cliquez dessus ?
Une fois ceci terminé, faites un commit avec le commentaire "Ex 2 : First routes" et poussez vers le serveur.
git add .
git commit -m 'Ex 2 : First routes'
git push
Les liens
Autre composant particulièrement utile, <Link /> sert, sans grande surprise, à afficher un lien vers une autre page.
Il s'utilise de la sorte :
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="page1">Page 1</Link>
</li>
<li>
<Link to="page2">Page 2</Link>
</li>
</ul>
</nav>
Remplacez l'élément a dans le menu "Random" par un élément Link. Comme vous pouvez l'observer en cliquant dessus, contrairement à l'élément a, le composant Link navigue vers une nouvelle adresse sans recharger la page.
Index et page par défaut
Le composant Route peut recevoir à la place d'une prop path une prop booléenne index, précisant que cette route se situe à la racine de l'adresse.
Ajoutez une route affichant le composant ReviewList comme index de notre application.
Essayez d'aller à l'adresse suivante : http://localhost:3000/qwerty (en supposant que votre serveur de développement est sur le port 3000 bien sûr...). Pas très pratique...
Créez un composant NotFound dans un fichier src/components/NotFound.jsx. Il devra juste afficher un petit message d'erreur et un bouton permettant de revenir à l'accueil.
Pour afficher ce composant, créez une nouvelle route ayant pour prop path la chaîne de caractères * (wildcard). Cette route sera systématiquement affichée si aucune autre route déclarée avant n'est affichée.
Une fois ceci terminé, faites un commit avec le commentaire "Ex 4 : Links, index and fallback" et poussez vers le serveur.
git add .
git commit -m 'Ex 4 : Links, index and fallback'
git push
Les paramètres de route
Maintenant que nous savons afficher quelques routes statiques, notre application aurait bien besoin de quelques routes dynamiques pour pouvoir afficher le contenu d'une critique en fonction de l'URL.
Modifiez votre composant ReviewList pour que chaque "carte" de critique soit un lien qui emmène l'utilisateur vers une page /view/<review.slug>.
Il nous faut maintenant préciser à React Router que notre route peut prendre un paramètre. Il faudra ensuite récupérer ce paramètre dans une variable afin de trouver la critique associée au "slug" passé dans l'URL.
Pour vous inspirer, voici deux exemples : une route avec un paramètre nommé name et un composant qui récupère ce paramètre et l'affiche dans un paragraphe.
<Route path="names/:name" element={<DisplayName />}>
import { useParams } from 'react-router-dom'
const DisplayName = () => {
const params = useParams()
return <p>{params.name}</p>
}
Critique aléatoire
Comme vous pouvez le remarquer en cliquant sur le lien "Random" dans le menu, ce dernier ne fonctionne plus correctement...
Modifiez votre composant Review pour lui faire afficher une critique aléatoire quand il reçoit la chaîne de caractères "random" comme paramètre.
Il existe une méthode Math.random() qui retourne un chiffre aléatoire entre zéro et un...
Une fois ceci terminé, faites un commit avec le commentaire "Ex 5 : Review display and random reviews" et poussez vers le serveur.
git add .
git commit -m 'Ex 5 : Review display and random reviews'
git push
Félicitations d'en être arrivés jusqu'ici, notre petite application de critique de vins fonctionne ! Nous allons y ajouter davantage de fonctionnalités lors de la seconde partie de ce TP.