🗺️ Introduction au routing côté client

React Router permet de créer des Single Page Applications (SPA) avec plusieurs "pages" sans rechargement du navigateur. L'URL change, mais React re-rend simplement les composants correspondants.

Dans ce module, les exemples montrent la syntaxe React Router v6 (la version actuelle). En production, installez react-router-dom via npm. Pour les mini-projets CDN, nous simulons le routing avec du state.

⚙️ Installation et configuration

# Installation
npm install react-router-dom

# Structure recommandée
src/
  pages/
    Home.jsx
    About.jsx
    Profile.jsx
    NotFound.jsx
  components/
    Navbar.jsx
  App.jsx
  main.jsx
// main.jsx — BrowserRouter englobe tout
import { BrowserRouter } from 'react-router-dom';
import App from './App';

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

🛣️ Routes de base

import { Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import NotFound from './pages/NotFound';

function App() {
  return (
    <div>
      <Navbar />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/users/:id" element={<UserDetail />} />
        <Route path="*" element={<NotFound />} /> {/* catch-all */}
      </Routes>
    </div>
  );
}

// Routes imbriquées (nested routes)
function App() {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index element={<Home />} />
        <Route path="about" element={<About />} />
        <Route path="blog">
          <Route index element={<BlogList />} />
          <Route path=":slug" element={<BlogPost />} />
        </Route>
      </Route>
    </Routes>
  );
}

// Layout avec Outlet (emplacement des routes enfants)
import { Outlet } from 'react-router-dom';
function Layout() {
  return (
    <div>
      <Navbar />
      <main>
        <Outlet /> {/* les routes enfants s'affichent ici */}
      </main>
    </div>
  );
}

🔢 Paramètres URL et Query String

import { useParams, useSearchParams, useLocation } from 'react-router-dom';

// useParams — paramètres dynamiques (/users/:id)
function UserDetail() {
  const { id } = useParams();
  // URL /users/42 → id = "42"
  return <p>Profil utilisateur #{id}</p>;
}

// useSearchParams — query string (?page=2&sort=date)
function BlogList() {
  const [searchParams, setSearchParams] = useSearchParams();
  const page = parseInt(searchParams.get('page') || '1');
  const sort = searchParams.get('sort') || 'date';

  function nextPage() {
    setSearchParams({ page: page + 1, sort });
  }
  return (
    <div>
      <p>Page {page} — Tri : {sort}</p>
      <button onClick={nextPage}>Page suivante</button>
    </div>
  );
}

// useLocation — infos sur l'URL actuelle
function Page() {
  const location = useLocation();
  // location.pathname, location.search, location.hash, location.state
  return <p>Route actuelle : {location.pathname}</p>;
}

🔒 Routes protégées

import { Navigate, Outlet } from 'react-router-dom';

// Composant de protection
function RequireAuth({ children }) {
  const { user } = useAuth();
  if (!user) {
    return <Navigate to="/login" replace />;
  }
  return children;
}

// Ou avec Outlet pour les routes imbriquées
function ProtectedLayout() {
  const { user } = useAuth();
  if (!user) return <Navigate to="/login" replace />;
  return <Outlet />;
}

// Usage dans App
function App() {
  return (
    <Routes>
      <Route path="/login" element={<Login />} />
      <Route element={<ProtectedLayout />}>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/profile" element={<Profile />} />
      </Route>
    </Routes>
  );
}
📝 Exercices R10 ▶ Mini-projet : SPA Router 🧠 QCM R10 Suivant : Performance →