Navigation côté client dans les SPA
Utilisez les flèches, cliquez ou glissez pour naviguer
1. Installer React Router
npm install react-router-dom
2. Créer des routes
BrowserRouter, Routes, Route
3. Naviguer sans rechargement
Link et NavLink
4. Paramètres dynamiques
useParams pour :id
5. Routes imbriquées
Outlet
6. Routes protégées
Redirection
7. Query params
useSearchParams
Le problème des SPA
Une seule page HTML, comment gérer plusieurs "pages" ?
React Router
Installation et configuration de base
Demo live : 3 pages
Home, About, Contact avec navigation
Routes dynamiques
useParams pour les pages produit/:id
Routes protégées
Redirection si non connecté
Une SPA = une seule page HTML
Sites traditionnels (MPA)
Single Page Application
Comment gérer plusieurs "pages" sans recharger ?
Solution : React Router simule la navigation côté client
Le routeur standard de l'écosystème React
Gère l'URL et affiche le composant correspondant
Sans rechargement de page !
npm install react-router-dom
react-router-dom est le package pour les applications web
Il existe aussi react-router-native pour React Native
// Vérifier la version installée
npm list react-router-dom
Le composant qui enveloppe toute l'application
Il utilise l'API History du navigateur pour gérer l'URL
// main.jsx ou App.jsx
import { BrowserRouter } from 'react-router-dom';
<BrowserRouter>
<App />
</BrowserRouter>
Oublier BrowserRouter = les routes ne fonctionnent pas !
Routes
Conteneur pour toutes les routes
Remplace l'ancien Switch
Route
Définit une seule route
path + element
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
L'URL change le composant affiché automatiquement
// App.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import Contact from './pages/Contact';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</BrowserRouter>
);
}
3 pages, 3 composants, 1 fichier HTML
Navigation sans rechargement
<a href>
Recharge la page
Pert tout le state React
<Link to>
Change l'URL en JS
Garde le state React
import { Link } from 'react-router-dom';
<nav>
<Link to="/">Accueil</Link>
<Link to="/about">Ă€ propos</Link>
<Link to="/contact">Contact</Link>
</nav>
to au lieu de href
Même apparence qu'un lien normal, mais comportement différent
Le contenu entre les balises devient le texte du lien
Link avec une classe spéciale quand la route est active
import { NavLink } from 'react-router-dom';
<NavLink
to="/about"
className={({ isActive }) => isActive ? 'text-blue-500 font-bold' : 'text-gray-500'}
>
Ă€ propos
</NavLink>
Par défaut, NavLink ajoute la classe active
Utile pour les menus de navigation
Capturer des valeurs de l'URL
/product/:id
Les deux-points indiquent un paramètre dynamique
// Définition de la route
<Route path="/product/:id" element={<Product />} />
// Composant Product.jsx
import { useParams } from 'react-router-dom';
function Product() {
const { id } = useParams();
return <h1>Produit #{id}</h1>;
}
URL: /product/42 affiche "Produit #42"
function Product() {
const { id } = useParams();
const [product, setProduct] = useState(null);
useEffect(() => {
// Fetch les données avec l'ID
fetch(`/api/products/${id}`)
.then(res => res.json())
.then(setProduct);
}, [id]);
if (!product) return <p>Chargement...</p>;
return (
<div>
<h1>{product.name}</h1>
<p>{product.price} EUR</p>
</div>
);
}
L'ID change = useEffect se relance automatiquement
Garder un layout commun
Dashboard avec sidebar commune
Seul le contenu principal change
Outlet affiche le composant de la sous-route
// Layout.jsx
import { Outlet } from 'react-router-dom';
function Layout() {
return (
<div className="flex">
<Sidebar />
<main>
<Outlet /> // Les sous-routes s'affichent ici
</main>
</div>
);
}
Outlet = "trou" oĂą les sous-routes s'affichent
<Route path="/dashboard" element={<Layout />}>
<Route index element={<DashboardHome />} />
<Route path="profile" element={<Profile />} />
<Route path="settings" element={<Settings />} />
</Route>
/dashboard
DashboardHome
/dashboard/profile
Profile
/dashboard/settings
Settings
index = route par défaut quand on visite /dashboard
Restreindre l'accès aux utilisateurs connectés
Redirection automatique si non connecté
import { useNavigate } from 'react-router-dom';
function Login() {
const navigate = useNavigate();
const handleLogin = () => {
// Après connexion réussie
navigate('/dashboard');
};
return <button onClick={handleLogin}>Se connecter</button>;
}
navigate() remplace history.push() de v5
navigate(-1) pour revenir en arrière
function ProtectedRoute({ children }) {
const { user } = useAuth(); // Votre contexte d'auth
const navigate = useNavigate();
if (!user) {
return <Navigate to="/login" replace />;
}
return children;
}
// Utilisation
<Route
path="/admin"
element={
<ProtectedRoute>
import { Navigate } from 'react-router-dom';
<Navigate to="/login" replace />
useNavigate()
Redirection dans un event handler
Après un clic, un submit...
Navigate
Redirection dans le JSX
Rendu conditionnel...
replace évite que l'utilisateur revienne avec le bouton "Retour"
Gérer les query parameters
/products?category=electronics&sort=price
Les paramètres après le ?
import { useSearchParams } from 'react-router-dom';
function Products() {
const [searchParams, setSearchParams] = useSearchParams();
const category = searchParams.get('category');
const sort = searchParams.get('sort');
// Modifier les params
const setCategory = (cat) => {
setSearchParams({ category: cat, sort: sort });
};
}
searchParams.get() retourne null si le param n'existe pas
function ProductFilters() {
const [searchParams, setSearchParams] = useSearchParams();
const updateFilter = (key, value) => {
const params = new URLSearchParams(searchParams);
if (value) {
params.set(key, value);
} else {
params.delete(key);
}
setSearchParams(params);
};
return (
<select onChange={(e) => updateFilter('category', e.target.value)}>
<option value="">Toutes</option>
<option value="electronics">Électronique</option>
</select>
);
}
L'URL change = partageable et bookmarkable !
Gérer les URLs inconnues
path="*"
Capture toutes les routes non définies
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>
Sans cette route, l'utilisateur voit une page vide
// NotFound.jsx
function NotFound() {
return (
<div className="text-center p-10">
<h1>404 - Page non trouvée</h1>
<Link to="/">Retour Ă l'accueil</Link>
</div>
);
}
Utiliser <a href> au lieu de <Link to>
La page se recharge et tout le state React est perdu !
Utilisez toujours Link pour la navigation interne
Oublier le BrowserRouter
Routes, Link, useParams... ne fonctionnent pas sans lui
Enveloppez toute l'application avec BrowserRouter
Path sans / initial dans les sous-routes
path="profile" est relatif, path="/profile" est absolu
Dans les routes imbriquées, utilisez des chemins relatifs
Ne pas gérer la route 404
L'utilisateur voit une page vide sur une URL inconnue
Ajoutez toujours une route path="*"
L'erreur
<a href="/about">
Ă€ propos
</a>
Recharge la page
Pert le state React
RequĂŞte serveur inutile
La solution
<Link to="/about">
Ă€ propos
</Link>
Change l'URL en JS
Garde le state React
Navigation instantanée
React Router ne recharge PAS la page
Il change le composant affiché selon l'URL
Link remplace <a> pour la navigation interne
Pas de rechargement, pas de perte de state
useParams() pour les paramètres dynamiques
:id dans l'URL devient accessible dans le composant
Outlet pour les routes imbriquées
Permet de garder un layout commun
useSearchParams pour les query parameters
?category=electronics&sort=price
BrowserRouter
Enveloppe l'app
Routes / Route
Définit les chemins
Link / NavLink
Navigation sans reload
useParams
/:id dynamique
Outlet
Routes imbriquées
Navigate
Redirection
useSearchParams pour les filtres et query params
path="*" pour la page 404
1. Créer une navigation 3 pages
Home, About, Contact avec Link
2. Page produit dynamique
/product/:id avec useParams
3. Dashboard avec routes imbriquées
Layout + Outlet pour sidebar commune
4. Route protégée
Redirection vers /login si non connecté
5. Filtres avec useSearchParams
?category=electronics&sort=price
Link to
Jamais <a href> pour la navigation interne
useParams
:id dans path = { id } dans le composant
Outlet
Point d'insertion pour les sous-routes
path="*"
Toujours prévoir la page 404
React Router = SPA avec vraies URLs
Navigation fluide, state préservé, bookmarks possibles
React Router est essentiel pour les SPA React
Prochaine étape : pratiquer avec des exercices !