Table des matières

Routage

Documentation React Router v7

SmartMaker utilise React Router pour gérer la navigation entre les pages de l'application (Single Page Application).

Configuration de base

Créer le Router

// src/components/app/Router/index.jsx

import { BrowserRouter, Routes, Route } from 'react-router-dom';

import { LoginPage } from '../../pages/public/LoginPage';
import { HomePage } from '../../pages/private/HomePage';
import { Error404Page } from '../../pages/errors/Error404Page';

export const Router = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/login" element={<LoginPage />} />
        <Route path="/" element={<HomePage />} />
        <Route path="*" element={<Error404Page />} />
      </Routes>
    </BrowserRouter>
  );
};

Intégrer dans App.jsx

// src/App.jsx

import { Provider } from '@cap-rel/smartcommon';
import { Router } from './components/app/Router';
import { config } from './appConfig';

export const App = () => {
  return (
    <Provider config={config}>
      <Router />
    </Provider>
  );
};

Routes protégées

Guards d'authentification

// src/components/app/Router/Guards/index.jsx

import { Outlet, Navigate } from 'react-router-dom';
import { useGlobalStates } from '@cap-rel/smartcommon';

/**
 * Routes publiques (login, register, etc.)
 * Redirige vers / si déjà connecté
 */
export const PublicRoutes = () => {
  const gst = useGlobalStates();
  const session = gst.get('session');

  if (session) {
    return <Navigate to="/" replace />;
  }

  return <Outlet />;
};

/**
 * Private routes (home, settings, etc.)
 * Redirects to /login if not authenticated
 */
export const PrivateRoutes = () => {
  const gst = useGlobalStates();
  const session = gst.get('session');

  if (!session) {
    return <Navigate to="/login" replace />;
  }

  return <Outlet />;
};

Utiliser les Guards

// src/components/app/Router/index.jsx

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { PublicRoutes, PrivateRoutes } from './Guards';

// Pages publiques
import { WelcomePage } from '../../pages/public/WelcomePage';
import { LoginPage } from '../../pages/public/LoginPage';

// Pages privées
import { HomePage } from '../../pages/private/HomePage';
import { SettingsPage } from '../../pages/private/SettingsPage';
import { ItemPage } from '../../pages/private/ItemPage';

// Erreurs
import { Error404Page } from '../../pages/errors/Error404Page';

export const Router = () => {
  return (
    <BrowserRouter>
      <Routes>
        {/* Routes publiques */}
        <Route element={<PublicRoutes />}>
          <Route path="/welcome" element={<WelcomePage />} />
          <Route path="/login" element={<LoginPage />} />
        </Route>

        {/* Routes privées */}
        <Route element={<PrivateRoutes />}>
          <Route path="/" element={<HomePage />} />
          <Route path="/settings" element={<SettingsPage />} />
          <Route path="/items/:id" element={<ItemPage />} />
        </Route>

        {/* Fallback 404 */}
        <Route path="*" element={<Error404Page />} />
      </Routes>
    </BrowserRouter>
  );
};

Hook useNavigation

SmartCommon expose useNavigation qui retourne un objet avec des methodes de navigation :

import { useNavigation } from '@cap-rel/smartcommon';

const MyComponent = () => {
  const nav = useNavigation();

  return (
    <div>
      {/* Navigation simple */}
      <button onClick={() => nav.navigate('/')}>Accueil</button>
      <button onClick={() => nav.navigate('/settings')}>Paramètres</button>

      {/* Avec paramètres */}
      <button onClick={() => nav.navigate(`/items/${itemId}`)}>Voir l'item</button>

      {/* Retour en arrière */}
      <button onClick={() => nav.navigate(-1)}>Retour</button>

      {/* Remplacer l'historique (builder chainable) */}
      <button onClick={() => nav.replace().to('/login')}>
        Déconnexion
      </button>
    </div>
  );
};

Propriétés retournées

Propriété Description
nav.navigate(to, options) Fonction de navigation React Router
nav.params Paramètres de la route (useParams)
nav.searchParams Query string (useSearchParams)
nav.location Objet location courant
nav.history Historique de navigation
nav.replace() Builder chainable : remplacer au lieu de pousser
nav.state(value) Builder chainable : passer un state
nav.to(path) Builder chainable : executer la navigation

Pour les liens simples, utilisez le composant Link :

import { Link } from 'react-router-dom';

const Navigation = () => {
  return (
    <nav>
      <Link to="/">Accueil</Link>
      <Link to="/settings">Paramètres</Link>
      <Link to="/items/123">Item 123</Link>
    </nav>
  );
};

Paramètres de route

Paramètres dynamiques

// Route avec paramètre :id
<Route path="/items/:id" element={<ItemPage />} />

// Récupérer le paramètre
import { useParams } from 'react-router-dom';

const ItemPage = () => {
  const { id } = useParams();  // id = "123" pour /items/123

  return <div>Item #{id}</div>;
};

Paramètres multiples

// Route avec plusieurs paramètres
<Route path="/users/:userId/posts/:postId" element={<PostPage />} />

const PostPage = () => {
  const { userId, postId } = useParams();
  // ...
};

Query strings

import { useSearchParams } from 'react-router-dom';

const SearchPage = () => {
  const [searchParams, setSearchParams] = useSearchParams();

  // Lire : /search?q=test&page=2
  const query = searchParams.get('q');     // "test"
  const page = searchParams.get('page');   // "2"

  // Modifier
  const handleSearch = (newQuery) => {
    setSearchParams({ q: newQuery, page: '1' });
  };

  return (
    <input
      value={query || ''}
      onChange={(e) => handleSearch(e.target.value)}
    />
  );
};

Routes imbriquées

Layout partagé

// src/components/app/Router/index.jsx

<Routes>
  <Route element={<PrivateRoutes />}>
    {/* Layout avec navigation bottom */}
    <Route element={<MainLayout />}>
      <Route path="/" element={<HomePage />} />
      <Route path="/search" element={<SearchPage />} />
      <Route path="/profile" element={<ProfilePage />} />
    </Route>

    {/* Pages sans navigation bottom */}
    <Route path="/items/:id" element={<ItemPage />} />
    <Route path="/settings" element={<SettingsPage />} />
  </Route>
</Routes>

Composant Layout

// src/components/layouts/MainLayout/index.jsx

import { Outlet } from 'react-router-dom';
import { useNavigation } from '@cap-rel/smartcommon';

export const MainLayout = () => {
  const nav = useNavigation();

  return (
    <div className="min-h-screen flex flex-col">
      {/* Contenu de la page (Outlet = enfant de la route) */}
      <main className="flex-1">
        <Outlet />
      </main>

      {/* Navigation fixe en bas */}
      <nav className="bg-white shadow-lg p-2 flex justify-around">
        <button onClick={() => nav.navigate('/')}>Accueil</button>
        <button onClick={() => nav.navigate('/search')}>Recherche</button>
        <button onClick={() => nav.navigate('/profile')}>Profil</button>
      </nav>
    </div>
  );
};

Animations de transition

Configuration dans appConfig

// appConfig.js

export const config = {
  pages: {
    // Depuis la home
    "/": {
      "/settings": "slideLeft",   // Home → Settings : slide vers la gauche
      "/items/*": "slideLeft",    // Home → Item : slide vers la gauche
      "*": "fade",                // Autres : fade
    },
    // Depuis settings
    "/settings": {
      "/": "slideRight",          // Settings → Home : slide vers la droite
    },
    // Par défaut
    "*": "fade",
  },
};

Animations disponibles

Animation Description
fade Fondu enchaîné
slideLeft Glissement vers la gauche
slideRight Glissement vers la droite
zoom Zoom avant/arrière

Voir Animations pour plus de détails.

Gestion d'erreurs

Page 404

// src/components/pages/errors/Error404Page/index.jsx

import { useNavigation } from '@cap-rel/smartcommon';

export const Error404Page = () => {
  const nav = useNavigation();

  return (
    <div className="min-h-screen flex flex-col items-center justify-center p-4">
      <h1 className="text-6xl font-bold text-gray-300 mb-4">404</h1>
      <p className="text-gray-600 mb-8">Page non trouvée</p>
      <button
        onClick={() => nav.navigate('/')}
        className="bg-primary text-white px-6 py-3 rounded-lg"
      >
        Retour à l'accueil
      </button>
    </div>
  );
};

Redirection conditionnelle

import { Navigate, useLocation } from 'react-router-dom';
import { useGlobalStates } from '@cap-rel/smartcommon';

const RequireAuth = ({ children }) => {
  const gst = useGlobalStates();
  const session = gst.get('session');
  const location = useLocation();

  if (!session) {
    // Sauvegarder l'URL pour rediriger après login
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  return children;
};

Bonnes pratiques

Structure des routes

Performance

Voir aussi