• 12 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 2/23/24

Transformez votre application en Single Page Application avec React Router

Comprenez le principe de SPA

Prenons notre machine à voyager dans le temps et retournons aux débuts du Web. 🤖

À cette époque, l’immense majorité des sites consistaient en un groupe de pages, envoyées par le serveur, qui s’affichaient en fonction de la navigation. Pour chaque interaction, telle que l’envoi d’un formulaire, la page entière devait être rechargée.

Mais au début des années 2000, le concept de Single Page Application (SPA) commence à émerger. Les idées principales derrière ce concept sont les suivantes :

  • Les utilisateurs ne chargent une page Web qu’une seule fois (le fameuxindex.html).

  • Au lieu de récupérer toute la page avec une requête HTTP, on les récupère de manière distincte, petite partie par petite partie, ce qui permet à l’utilisateur d’interagir de manière beaucoup plus dynamique.

  • L’utilisateur peut naviguer entre plusieurs pages et JavaScript (et dans notre cas, React) gère l’affichage de nouvelles pages au sein du même domaine, sans qu’un rafraîchissement complet de la page soit nécessaire.

Un site web classique se rafraîchit à chaque requête envoyée vers le serveur. Pour une Single Page Application, le JavaScript déterminera les éléments distincts à rafraîchir après une requête.
L’affichage du site Web classique en haut est moins dynamique que celui de la SPA en bas.

Mais… est-ce que ça veut dire que les SPA sont nécessairement mieux ? Et donc que tous les nouveaux sites sont codés pour être des SPA ?

Eh bien, non. Toutes les applications ne sont pas nécessairement des SPA. Lorsque vous codez votre site en Single Page Application, il faut être conscient de certains inconvénients : vos utilisateurs doivent notamment impérativement avoir JavaScript pour que votre site fonctionne, ou encore que le Search Engine Optimisation (SEO, l’optimisation de l’indexation de votre site par les moteurs de recherche) est plus laborieux pour les Single Page Applications.

Les projets que nous créons avec Create React App ne peuvent pas encore être considérés comme des Single Page Applications : il leur manque une solution de routing.

Découvrez React Router

Contrairement aux frameworks comme Angular, React ne nous fournit pas directement une solution pour gérer les routes de notre application. Pas de panique, comme pour quasiment tout en React, l’écosystème a vite comblé ce besoin. Il existe donc plusieurs solutions de routing. Celle à laquelle nous allons nous intéresser dans ce cours est React Router (le nom est plutôt bien trouvé, n’est-ce pas ? 🙃).

 Du routing ? Très bien, mais concrètement, ça veut dire quoi une route ?

Comme nous pouvons le voir dans la documentation React Router, une route permet d’afficher des composants de manière conditionnelle si le path (chemin) de l’URL correspond au path de la route.

On lui passe en prop le path auquel la route correspond et elle se charge d’afficher les children qui lui sont passés.

Cette bibliothèque, créée par Remix, met à votre disposition tous les outils nécessaires pour gérer la navigation dans votre application côté client.

Alors, partons à la découverte de React Router. 🚀

Créez votre premier fichier de routing

Nous allons commencer par installer la bibliothèque avecyarn add react-router-dom@6.10.0. Si vous voulez en apprendre davantage sur la configuration, n’hésitez pas à jeter un œil à la documentation de React Router.

React Router est maintenant prêt à être utilisé ! 🎉

Actuellement, nous n’avons qu’une seule fonctionnalité avecHome. Créons dès maintenant un nouveau composant pour le questionnaire.

Pour cela, on crée un dossierSurvey.jsxdanspages. Pour le moment, gardons un composant très simple :

function Survey() {
    return (
        <div>
            <h1>Questionnaire 🧮</h1>
        </div>
    )
}

export default Survey

Votre mission, si vous l’acceptez, est de pouvoir naviguer entre la page d’accueil – Home – et le questionnaire – Survey. 🕵️‍♀️

Vous vous en doutez sûrement : nous allons utiliser React Router et ses composants BrowserRouter, Routes et Route !

Dans l’exemple ci-dessous, on renomme BrowserRouter en Router pour une lecture plus simple. Il servira à stocker et à s’abonner au changement de l’URL de la page courante (celle qu’on retrouve dans la barre d’URL).

Ensuite, nous ajoutons le composant Routes qui va servir à sélectionner le composant enfant correspondant à la location.

Finalement, le composant le plus complexe, le composant Route. Ce composant prend de base plusieurs paramètres dont à minima :

  • pathqui contient l’URL dans notre navigateur qui dirigera vers le composant ;

  • elementqui va permettre de sélectionner le composant à afficher.

Le fichierindex.jsxà la racine de notre projet se transforme donc de cette manière :

import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
import Home from './pages/Home/'
 
ReactDOM.render(
    <React.StrictMode>
        <Router>
            <Routes>
                <Route path="/" element={<Home />} />
            </Routes>
                  </Router>
    </React.StrictMode>,
document.getElementById('root')
)

L’idée est maintenant de mettre dans notre router toutes les routes qui seront accessibles.

La barre d’adresse est sur localhost:3000/survey
Il faudra afficher le bon composant pour cette URL.

Créons donc une route pour la page d’accueil et pour notre questionnaire.

import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'
import Home from './pages/Home/'
import Survey from './pages/Survey/'

ReactDOM.render(
  <React.StrictMode>
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/survey" element={<Survey />} />
      </Routes>
    </Router>
  </React.StrictMode>,
  document.getElementById('root')
)

Yay ! Si vous allez sur l’URL http://localhost:3000/, on a bien la page d’accueil qui s’affiche. C’est la même chose si vous vous mettez http://localhost:3000/survey dans la barre d’URL. Félicitations ! Tout fonctionne bien comme prévu ! 🚀

Mais bon… Ce n’est pas vraiment pratique de devoir taper toutes nos URL à la main dans la barre du navigateur pour changer de page. 🙈

Naviguez avec les Link

Profitons-en pour créer notre header, avec les liens vers les différentes pages de notre application.

Dans notre dossier/components, on crée donc un nouveau dossier/Headeravec un fichierindex.jsxà l’intérieur, ce qui nous donne/components/Header/index.jsx:

import { Link } from 'react-router-dom'
 
function Header() {
    return (
        <nav>
            <Link to="/">Accueil</Link>
            <Link to="/survey">Questionnaire</Link>
        </nav>
    )
}

export default Header

Ici, j’utiliseLink, qui nous vient de React Router et se comporte comme une baliseanchor. Il est donc très important de l’utiliser lorsque vous souhaitez naviguer pour l'accessibilité de votre application (et non utiliser des redirections déclenchées par desonClick).

Utilisons maintenantHeaderdansindex.jsxà la racine de notre projet :

import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'
import Home from './pages/Home/'
import Survey from './pages/Survey/'
import Header from './components/Header'

ReactDOM.render(
  <React.StrictMode>
    <Router>
      <Header />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/survey" element={<Survey />} />
      </Routes>
    </Router>
  </React.StrictMode>,
  document.getElementById('root')
)
Notre page d'accueil et notre questionnaire ont les liens pour naviguer entre composants.
Nos routes renvoient bien les bons composants.

Vous avez maintenant la base de votre application avec navigation : félicitations à vous, vous avez fait du bon boulot. 🚀

Maintenant que nous avons vu comment mettre en place le routing, j’en profite pour vous montrer comment découper notre router quand nous avons beaucoup de routes à gérer – dans un projet de code plus important, par exemple.

Utilisez les Outlets pour afficher certaines parties de la page

Dans une application complexe, on peut décider d’afficher certaines parties de la page en fonction de la route que nous avons prise. Imaginons que pour notre questionnaire nous souhaitions afficher des questions différentes si la personne est porteuse de projet ou prestataire freelance.

Nous commençons donc par modifier notre composant Survey comme ceci :

import { Outlet, Link } from 'react-router'

function Survey() {
  return (
    <div>
      <h1>Questionnaire 🧮</h1>
      <Link to="client">Questionnaire Client</Link>
      <Link to="freelance">Questionnaire Freelance</Link>
      <Outlet />
    </div>
  )
}
export default Survey

Ensuite il nous faut créer nos composants pour les formulaires client et prestataires. Je vais donc créer un dossierClientFormdans/components. Dans ce dossier, je crée mon fichierindex.jsavec le code suivant :

function ClientForm() {
  return (
    <div>
      <h2>Questionnaire Client</h2>
    </div>
  )
}
export default ClientForm

Je peux faire la même chose ensuite avec le composant FreelanceForm, puis finalement je viens modifier mon Router afin d’inclure les composants de ma page Questionnaire de cette manière :

import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'
import Home from './pages/Home/'
import Survey from './pages/Survey/'
import Header from './components/Header'
// On ajoute nos composants
import ClientForm from './components/ClientForm'
import FreelanceForm from './components/FreelanceForm'

ReactDOM.render(
  <React.StrictMode>
    <Router>
      <Header />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/survey" element={<Survey />}>
          { /* Nous imbriquons nos composants dans survey */}
          <Route path="client" element={<ClientForm />} />
          <Route path="freelance" element={<FreelanceForm />} />
        </Route>
      </Routes>
    </Router>
  </React.StrictMode>,
  document.getElementById('root')
)

Et voilà, notre affichage de survey dépendra maintenant de notre route :

  • /survey: n’affichera que l’en-tête et les deux liens ;

  • /survey/client: ajoutera le composant ClientForm ;

  • /survey/freelance: ajoutera le composant FreelanceForm.

Nos Outlets fonctionnent ! La page questionnaire affiche une interface différente en fonction de la route que nous demandons
Nos Outlets fonctionnent ! 🎉

Récupérez des paramètres dans vos URL

La navigation de notre application fonctionne bien, mais comment faire si vous voulez passer des paramètres ? Par exemple, lorsqu’on va faire le questionnaire et que le numéro de chaque question sera récupéré depuis l’URL ? 

Eh bien, bonne question ! Le router vous permet de récupérer des paramètres ; pour cela, il suffit d’écrire votre route comme ici dans le fichierindex.jsxà la racine de/src:

<Route path="/survey/:questionNumber" element={<Survey />} />

Danscomponents/Header/index.jsx, mettons donc un numéro de question à la suite :

function Header() {
    return (
        <nav>
            <Link to="/">Accueil</Link>
            <Link to="/survey/42">Questionnaire</Link>
        </nav>
    )
}

Allons maintenant récupérer ce paramètre dansSurvey/index.jsxà l’aide du hookuseParams, mis à disposition par React Router :

import { useParams } from 'react-router-dom'
 
function Survey() {
    const { questionNumber } = useParams()
 
    return (
        <div>
            <h1>Questionnaire 🧮</h1>
            <h2>Question {questionNumber}</h2>
        </div>
    )
}

Félicitations à vous ! Vous avez récupéré le numéro de votre question en paramètre. ☀️

Créez une route pour les attraper toutes : 404

Quelle chance, tout fonctionne comme on le souhaite ! Mais qu’est-ce qui se passe si je commence à taper n’importe quoi dans mon URL ? Par exemple, si j’essaie d'accéder au contenu de http://localhost:3000/coucouCommentCaVa ?

Notre header s’affiche, mais rien d’autre… Moi, j’aimerais signaler à l’utilisateur que rien n’existe à cette adresse. Eh bien, ça vous dit quelque chose, les pages d’erreur ? C'est ce que nous allons faire ici : afficher une page 404.

On commence par créer un simple composantErrordanscomponents/Error/index.jsx:

function Error() {
    return (
        <div>
            <h1>Oups 🙈 Cette page n'existe pas</h1>
        </div>
    )
}
 
export default Error

On retourne maintenant dans notre Router. Afin de gérer les erreurs pour les routes qui n’existent pas nous ajouterons une route avec unpathparticulier, lepath="*"ainsi toutes les routes qui ne sont pas mentionnées plus haut conduiront à ma page d’erreur.

Dans notre router, on a donc :

ReactDOM.render(
  <React.StrictMode>
    <Router>
      <Header />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/survey/:questionNumber" element={<Survey />} />
        <Route path="*" element={<Error />} />
      </Routes>
    </Router>
  </React.StrictMode>,
  document.getElementById('root')
)

Testons dans notre navigateur :

Une Route permet d'afficher une erreur lorsque l'URL saisie n'existe pas dans le Router
Notre Route permet d’afficher une erreur lorsque l’URL saisie n’existe pas dans le router.

Yes ! 💪

Exercez-vous

Vous avez la base de votre application, félicitations à vous. C’est maintenant le moment de voler de vos propres ailes.

Vous trouverez la base de l’exercice sur la branche P1C2-begin. Vos objectifs sont les suivants :

  • Créer une nouvelle pageResultset l’ajouter au router.

  • Créer une nouvelle pageFreelances, l’ajouter au router, et créer un lien dans leHeader.

  • Dans Survey/index.jsx, coder un lienprécédentet un liensuivantqui permettent respectivement de passer à la question précédente et à la question suivante. 

    • Si la question actuelle est à 1, le lien “précédent” reste sur la question 1. 

    • Si la question est à 10, le lien “suivant” ne s’affiche pas. À la place, il y aura un lien “Résultats” qui redirigera vers la page “Results”.

La solution se trouve sur P1C2-solution.

En résumé

  • Avec les Single Page Applications (SPA), l’utilisateur a l'impression de naviguer sur différentes pages, mais il n'existe qu’une seule page HTML sur laquelle le contenu est greffé avec JavaScript.

  • React Router est l’une des bibliothèques qui permettent de transformer une app React en SPA.

  • Les Outlets permettent d’ajouter des composants dans des routes enfants d’une page.

  • Les composants BrowserRouter, Routes et Route permettent de gérer l’affichage des différentes pages.

  • Il est possible de passer des paramètres dans une route et de les récupérer avecuseParams().

  • En indiquant une route avecpath="*"à la fin des routes, cela permet de capturer toutes les routes dont le path ne correspond à aucune route déclarée, et donc de créer une page 404.

Notre application contient maintenant les pages nécessaires pour la suite ! 🎉 Au prochain chapitre, nous allons sécuriser nos props avec les PropTypes. Alors, rendez-vous au prochain chapitre !

Example of certificate of achievement
Example of certificate of achievement