TP2 - React Router - Partie 2
#
Filtrage par catégorieVous l'avez certainement remarqué : nous avons déplacé dans le composant ReviewList
une liste déroulante (select
) cachée. Retirez-lui la classe hidden
afin de voir de quoi il retourne.
Comme vous pouvez le voir, il s'agit d'un filtre permettant de n'afficher qu'une certaine catégorie de vins. En l'état actuel, le choix est très limité.
select
#
Faire fonctionner la Modifiez cette liste déroulante de manière à afficher la liste de toutes les catégories du ficher JSON, que vous pouvez maintenant importer de la sorte :
import { reviews, categories } from '../api/db.json'
Afin de faire fonctionner cet élément (ainsi que le filtrage), notre composant devra se souvenir de l'option actuellement sélectionnée.
Faites les modifications appropriées pour implémenter ces fonctionnalités.
Une fois ceci terminé, faites un commit avec le commentaire "Ex 6 : Category filters" et poussez vers le serveur.
git add .git commit -m 'Ex 6 : Category filters'git push
#
Navigation programmatiqueImaginons maintenant qu'un internaute visitant votre application souhaite ajouter la page des "Bordeaux" en favoris. Comme vous vous en doutez, cela ne fonctionnera pas en l'état actuel : pour qu'un utilisateur puisse ajouter une catégorie comme favoris de son navigateur (ou la partager avec un ami sur Discord...), il nous faut modifier l'URL courante quand on sélectionne un élément de la liste déroulante.
Malheureusement, notre select
et ses option
s ne sont pas des éléments Link
, ils ne permettent donc pas directement de modifier l'adresse courante de manière déclarative. Pour répondre à ce type de contraintes, React Router expose un hook supplémentaire : useNavigate
.
Voici un exemple d'utilisation :
const CheckAge = () => { const [birth, setBirth] = useState('') const navigate = useNavigate()
const handleSubmit = e => { e.preventDefault() if(new Date().getFullYear() - new Date(birth).getFullYear() >= 18){ navigate('/adult-stuff') } else { navigate('/home') } } return <form onSubmit={handleSubmit}> <label htmlFor="birthdate">Date de naissance</label> <input id="birthdate" type="date" onChange={e => setBirth(e.target.value)} value={birth} /> <input type="submit" value="Valider" /> </form>}
Dans cet exemple, on souhaite vérifier l'age de l'utilisateur en lui demandant de rentrer sa date de naissance. On souhaite rediriger l'utilisateur vers du contenu pour adulte si il a plus de 18 ans, ou vers la page d'accueil si il est mineur.
On se sert donc de la fonction navigate
pour initier une navigation programmatique vers la page adaptée.
Utilisez cet exemple et vos connaissances sur les routes pour faire naviguer l'utilisateur quand il sélectionne un élément de la liste déroulante. Retirez ensuite le state de votre composant pour faire en sorte que la valeur courante de la liste déroulante soit contrôlée par le routeur à l'aide du hook useParams
.
Une fois ceci terminé, faites un commit avec le commentaire "Ex 7 : Params as value" et poussez vers le serveur.
git add .git commit -m 'Ex 7 : Params as value'git push
#
Login, redirections et pages protégéesPour cet exercice en deux parties, vous allez ajouter à votre application une page contenant un formulaire permettant d'ajouter une critique de vins. Cependant, cette page ne devra être accessible qu'à un utilisateur connecté.
#
Ajout de critiquesDans un premier temps, créez dans un fichier éponyme un composant AddReview
, ainsi qu'une route pour y accéder : /add
.
Ce composant devra comporter un formulaire contenant les champs :
- Author
- Title
- Wine
- Category
- Price
- Review
Essayez de composer cette page vous même : vous pouvez utiliser la bibliothèque BareCSS qui est déjà incluse dans ce projet.
Chacun de ces élément doit bien entendu être contrôlé, et la sélection de la catégorie devra se faire à l'aide d'une liste déroulante.
Cela n'a pas de réel intérêt pour la suite des exercices, mais reste une révision bienvenue.
Login
et Logout
#
Pages Créez un composant Login
dans un nouveau fichier Auth.jsx
. Ajoutez-y un formulaire très simple comportant juste un champs "Login" et un champs "Password". Lors de la soumission du formulaire, transmettez ces informations en appelant une fonction passée dans une prop onLogin
.
Créez ensuite un composant Logout
dans ce même fichier, qui sera une simple page de confirmation permettant à l'utilisateur de se déconnecter à l'aide d'un bouton.
N'oubliez pas d'exporter ces composants de votre fichier !
Créez ensuite deux routes, /login
et /logout
, qui conduiront l'utilisateur aux composants appropriés.
Nous n'allons évidemment pas implémenter une vraie authentification, mais le concept n'est pas très différent. Notre application doit juste garder en mémoire le fait que l'utilisateur soit connecté ou non, ainsi que son nom d'utilisateur.
Le composant qui sera le garant de cet état sera le composant App
, qui est le plus haut
dans le hiérarchie, et qui pourra ainsi passer ces informations en props aux composants enfants qui en auront besoin.
Ajoutez donc un state comprenant ces informations au composant App
. Créez les méthode handleLogin
et handleLogout
modifiant ce state et passez les respectivement aux
composant Login
et Logout
par leurs props.
Une fois ceci terminé, faites un commit avec le commentaire "Ex 8 : Authenticating users" et poussez vers le serveur.
git add .git commit -m 'Ex 8 : Authenticating users'git push
#
Redirections déclarativesNous avons maintenant toutes les pages que nous souhaitions ajouter, mais toutes ces pages sont accessibles par défaut : vous pouvez vous rendre sur /logout
sans être authentifié par exemple, ou aller sur la page /login
alors que vous êtes déjà authentifié, ou encore aller sur la page /add
sans être authentifié.
Il manque dans notre application des redirections. React Routeur propose plusieurs solutions pour ce faire : faire des appels à navigate
comme nous l'avons fait précedemment (méthode impérative), ou utiliser le composant Navigate
(méthode déclarative).
Pour notre cas d'usage, il est préférable d'utiliser le composant Navigate
: ce composant, quand il est affiché, redirige l'utilisateur vers l'adresse qui lui est donnée. On peut donc s'en servir avec de l'affichage conditionnel à l'aide d'une prop isAuthenticated
par exemple...
Essayez d'ajouter ce composant dans votre page AddReview
, de manière à ce que l'utilisateur soit redirigé vers la page Login
si il n'est pas authentifié.
tip
Allez observer la documentation de React Router pour ce composant Navigate
. Ce composant peut prendre une prop (booléenne) replace
. Essayez de lui passer, et observez la différence en jouant avec les boutons précédent et suivant de votre navigateur.
Une fois que vous aurez réussi, ajoutez les redirections suivantes :
- Rediriger l'utilisateur sur la page d'accueil depuis la page Login si il est authentifié
- Rediriger l'utilisateur sur la page Login depuis les pages AddReview et Logout si il n'est pas authentifié
#
Route stateMaintenant, quand un utilisateur souhaite ajouter une critique, il est redirigé vers la page de connection, puis une fois connecté vers la page d'accueil.
Il serait plus logique de le rediriger vers la page qu'il a demandé à l'origine, soit par exemple AddReview
.
Le composant Navigate
accepte une chaîne de caractères comme prop to
, mais peut aussi prendre un objet.
note
Pour information, l'objet que l'on peut passer peut prendre les propriétés suivantes :
pathname
: le chemin de l'URLsearch
: la querystring de l'URLhash
: le hash de l'URL (ancre HTML)
Par ailleurs, ce composant accepte aussi une prop supplémentaire : state
. Cette prop permet de stocker un état supplémentaire pour cette navigation.
La documentation actuelle n'est pas très claire à ce sujet.
On peut utiliser la propriété state
de cet objet pour passer des informations d'une page à l'autre sans les mettre dans l'URL. Dans notre cas, on peut par exemple stocker la page dont on vient, afin de pouvoir y rediriger l'utilisateur ensuite :
{/* Sur une page protégée */}{!user && <Navigate to={{ pathname: '/login' }} state={{ from: '/protected' }} replace/>}
On peut ensuite dans une route accéder à la valeur de cet objet state
à l'aide du hook useLocation
:
const LocationDisplay = () => { const location = useLocation() return <ul> <li> I'm on {location.pathname} </li> {location.state && <li> I'm coming from {location.state.from} </li>} </ul>}
A l'aide de ces informations faites en sorte de rediriger l'utilisateur sur la page à laquelle il essayait d'accéder une fois qu'il s'est authentifié.
Pour finaliser cet exercice, dans le composant Layout
, ajoutez à droite de la barre de navigation un lien qui affichera
"Login" si l'utilisateur est authentifié et "Logout" si il ne l'est pas, pointant sur la page correspondante.
Une fois ceci terminé, faites un commit avec le commentaire "Ex 9 : Redirecting & route state" et poussez vers le serveur.
git add .git commit -m 'Ex 9 : Redirecting & route state'git push
#
Les routes imbriquéesHistoire d'aller un peu plus loin, nous allons ajouter à notre application des routes imbriquées.
Allez voir sur la page du résultat de ce TP, et essayez de sélectionner une catégorie, puis de cliquer sur une critique. Comme vous pouvez le voir, la critique s'affiche au dessus de la select
des catégories. C'est un peu difficle à observer dans la correction, mais l'adresse dans la barre d'adresse est par exemple /pomerol/view/barrel-sample-173
.
React Router donne la possibilité d'ajouter des routes imbriquées : des routes dans des routes. Il suffit pour ce faire de déclarer une route à l'intérieur d'une autre route, comme par exemple :
<Routes> <Route path="users:userid" element={<User />}> <Route path="edit" element={<UserEdit />}> </Route><Routes>
Ici, l'utilisateur navigant sur /users/5
verra le composant User
. Si il navigue ensuite vers /users/5/edit
, il verra le composant UserEdit
à l'intérieur du composant User
.
Le composant User
, pour pouvoir utiliser cette fonctionnalité doit définir dans son code un endroit ou placer les élements imbriqués qu'il peut recevoir. Pour ce faire, React Router expose un composant Outlet
, qu'on peut utiliser de la sorte :
import {Outlet} from 'react-router-dom'
const User = () => { return <div> <h1>Page utilisateur</h1> {/* ... */} <Outlet /> </div>}
À l'aide de ces informations, modifiez vos routes ainsi que votre composant ReviewList
afin de pouvoir être capable d'afficher le composant Review
depuis l'adresse /:category/view/:slug
.
Une fois ceci terminé, faites un commit avec le commentaire "Ex 10 : Nested routes" et poussez vers le serveur.
git add .git commit -m 'Ex 10 : Nested routes'git push
#
Conclusion et perspectives d'améliorationFélicitations ! Vous savez maintenant utiliser React Router, du moins une bonne partie de ses fonctionnalités. N'hésitez pas à en explorer la documentation pour en apprendre davantage !
Pour améliorer encore votre application, vous pourriez par exemple :
- Lui ajouter une barre de recherche liée au routeur (query string)
- Proposer des options de tri de la liste (nom, prix...)
- Ajouter un système de pagination pour limiter le nombre par page
- Implémenter l'ajout de critique