React Router DOM v6: Crea Sistemas de Rutas en React

React Router DOM es una librería estándar para la gestión de rutas en aplicaciones React. Permite a los desarrolladores crear navegaciones dinámicas, permitiendo que los usuarios se desplacen entre diferentes vistas o componentes sin recargar la página. Con React Router DOM, puedes definir rutas, manejar parámetros de URL, proteger rutas privadas y más, todo sin perder la funcionalidad de una aplicación de una sola página (SPA). Su última versión (v6) trae mejoras en la simplicidad y flexibilidad de las rutas anidadas, la navegación programática y la carga diferida de componentes.

1. Instalación de React Router DOM

bash
# Usando npm
npm install react-router-dom

# Usando yarn
yarn add react-router-dom

# Usando pnpm
pnpm add react-router-dom

2. Configuración Básica

bash
src/
├── components/
├── pages/
│   ├── Home.jsx
│   ├── About.jsx
│   ├── Products.jsx
│   └── NotFound.jsx
├── layouts/
│   └── RootLayout.jsx
├── App.jsx
└── main.jsx

Configuración Inicial (main.jsx)

jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';

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

3. Creación de Páginas Básicas

En React Router DOM, una página se representa como un componente que se renderiza cuando una ruta específica es visitada. En esta sección, aprenderemos cómo crear páginas básicas, como la página de inicio y la página “Sobre nosotros”, y cómo integrarlas en nuestro sistema de rutas. Estas páginas son componentes simples que muestran contenido estático, pero se pueden expandir fácilmente para incluir funcionalidades más complejas conforme tu aplicación crece.

Páginas Home.jsx

jsx
function Home() {
return (
  <div className="container">
    <h1>Página de Inicio</h1>
    <p>Bienvenido a nuestra aplicación</p>
  </div>
);
}

export default Home;

Páginas About.jsx

jsx
function About() {
return (
  <div className="container">
    <h1>Sobre Nosotros</h1>
    <p>Información sobre nuestra empresa</p>
  </div>
);
}

export default About;

Páginas NotFound.jsx

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

function NotFound() {
return (
  <div className="container">
    <h1>404 - Página no encontrada</h1>
    <p>Lo sentimos, la página que buscas no existe.</p>
    <Link to="/">Volver al inicio</Link>
  </div>
);
}

export default NotFound;

4. Creación del Layout Principal

El Layout Principal en React Router DOM* es un componente que define la estructura común de tu aplicación, como el encabezado, la barra de navegación y el pie de página. Este componente se utiliza para envolver otras páginas y rutas dentro de la misma estructura visual, asegurando una experiencia de navegación coherente. En esta sección, aprenderás a crear un Layout Principal que incluya un sistema de navegación y cómo utilizar el componente Outlet para renderizar las rutas hijas dentro de este Layout.

RootLayout.jsx

RootLayout.jsx organiza el diseño principal de la aplicación React, permitiendo que se muestre el contenido dinámico de cada página dentro de una estructura común de navegación y pie de página. Usa NavLink para resaltar el enlace activo según la ruta actual, y Outlet para renderizar las rutas hijas. Esto permite mantener una interfaz consistente en toda la app.

jsx
import { Outlet, Link, NavLink } from 'react-router-dom';

function RootLayout() {
return (
  <>
    <header>
      <nav>
        <ul>
          <li>
            <NavLink 
              to="/"
              className={({ isActive }) => 
                isActive ? "active" : ""
              }
            >
              Inicio
            </NavLink>
          </li>
          <li>
            <NavLink 
              to="/about"
              className={({ isActive }) => 
                isActive ? "active" : ""
              }
            >
              Sobre Nosotros
            </NavLink>
          </li>
          <li>
            <NavLink 
              to="/products"
              className={({ isActive }) => 
                isActive ? "active" : ""
              }
            >
              Productos
            </NavLink>
          </li>
        </ul>
      </nav>
    </header>

    <main>
      <Outlet />
    </main>

    <footer>
      <p>© 2024 Mi Aplicación</p>
    </footer>
  </>
);
}

export default RootLayout;

5. Configuración de Rutas en App.jsx

Se define la estructura de navegación de la aplicación con rutas anidadas y parámetros dinámicos. Las rutas principales (Home, About, y NotFound) están organizadas bajo RootLayout, mientras que las rutas de productos (ProductList y ProductDetail) están agrupadas dentro de ProductLayout, permitiendo una navegación más organizada y específica, especialmente útil para listas de productos y detalles.

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

function App() {
return (
  <Routes>
    <Route path="/" element={<RootLayout />}>
      <Route index element={<Home />} />
      <Route path="about" element={<About />} />
      <Route path="products" element={<Products />} />
      <Route path="*" element={<NotFound />} />
    </Route>
  </Routes>
);
}

export default App;

6. Rutas Anidadas y Parámetros

Este código configura rutas en la app usando React Router. Bajo RootLayout, define las rutas principales (Home, About) y anida las rutas de products para mostrar una lista (ProductList) o detalles específicos (ProductDetail) de cada producto según su id, manejando además una ruta de error (NotFound).

jsx
import { Routes, Route } from 'react-router-dom';
import ProductList from './pages/ProductList';
import ProductDetail from './pages/ProductDetail';
import ProductLayout from './layouts/ProductLayout';

function App() {
return (
  <Routes>
    <Route path="/" element={<RootLayout />}>
      <Route index element={<Home />} />
      <Route path="about" element={<About />} />
      
      {/* Rutas anidadas de productos */}
      <Route path="products" element={<ProductLayout />}>
        <Route index element={<ProductList />} />
        <Route path=":id" element={<ProductDetail />} />
      </Route>
      
      <Route path="*" element={<NotFound />} />
    </Route>
  </Routes>
);
}

ProductDetail.jsx con Parámetros

Este componente ProductDetail obtiene el parámetro id de la URL mediante useParams y muestra los detalles de un producto específico. Además, utiliza useNavigate para regresar a la página anterior cuando se hace clic en el botón “Volver”.

jsx
import { useParams, useNavigate } from 'react-router-dom';

function ProductDetail() {
const { id } = useParams();
const navigate = useNavigate();

return (
  <div>
    <h2>Detalles del Producto {id}</h2>
    <button onClick={() => navigate(-1)}>
      Volver
    </button>
  </div>
);
}

7. Navegación Programática

En LoginPage, se utiliza useNavigate para redirigir al usuario al dashboard después de un inicio de sesión exitoso. Al enviar el formulario (handleLogin), intenta iniciar sesión y, si es exitoso, navega a /dashboard con un mensaje en el estado de la navegación.

jsx
import { useNavigate } from 'react-router-dom';

function LoginPage() {
const navigate = useNavigate();

const handleLogin = async (e) => {
  e.preventDefault();
  // Lógica de login aquí
  try {
    await loginUser();
    navigate('/dashboard', { 
      replace: true,
      state: { message: 'Login exitoso' } 
    });
  } catch (error) {
    console.error('Error en login:', error);
  }
};

return (
  <form onSubmit={handleLogin}>
    {/* Formulario aquí */}
  </form>
);
}

8. Rutas Protegidas

Este código crea una ruta protegida en React Router usando ProtectedRoute, que verifica la autenticación del usuario (checkAuth()). Si el usuario no está autenticado, redirige a /login, pasando la ubicación actual en el estado para facilitar la redirección posterior. En caso contrario, muestra el componente hijo (Dashboard).

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

function ProtectedRoute({ children }) {
const isAuthenticated = checkAuth(); // Tu lógica de autenticación
const location = useLocation();

if (!isAuthenticated) {
  return <Navigate to="/login" state={{ from: location }} replace />;
}

return children;
}

// Uso en App.jsx
function App() {
return (
  <Routes>
    <Route path="/" element={<RootLayout />}>
      <Route index element={<Home />} />
      <Route path="login" element={<Login />} />
      <Route
        path="dashboard"
        element={
          <ProtectedRoute>
            <Dashboard />
          </ProtectedRoute>
        }
      />
    </Route>
  </Routes>
);
}

9. Manejo de Loading States

Este código implementa Suspense de React para mostrar un componente de carga (LoadingSpinner) mientras Dashboard se carga de forma asíncrona. Útil para mejorar la experiencia del usuario, especialmente si Dashboard tarda en cargarse.

jsx
import { Suspense } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';

function LoadingSpinner() {
return <div>Cargando...</div>;
}

function App() {
return (
  <Routes>
    <Route path="/" element={<RootLayout />}>
      <Route
        path="dashboard/*"
        element={
          <Suspense fallback={<LoadingSpinner />}>
            <Dashboard />
          </Suspense>
        }
      />
    </Route>
  </Routes>
);
}

10. Estilos para la Navegación

Este CSS estiliza los enlaces de navegación.

css
/* styles/navigation.css */
.nav-link {
color: #333;
text-decoration: none;
padding: 0.5rem 1rem;
}

.nav-link:hover {
color: #666;
}

.active {
color: #007bff;
font-weight: bold;
}

11. Buenas Prácticas

  1. Organización de Rutas Este código optimiza la carga de componentes usando lazy loading, organiza las rutas en un archivo centralizado (routes/index.jsx) y estructura las rutas de manera jerárquica con un layout base, lo que mejora el rendimiento y facilita el mantenimiento de la aplicación.
jsx
// routes/index.jsx
import { lazy } from 'react';

const Home = lazy(() => import('../pages/Home'));
const About = lazy(() => import('../pages/About'));

export const routes = [
{
  path: '/',
  element: <RootLayout />,
  children: [
    { index: true, element: <Home /> },
    { path: 'about', element: <About /> },
    // ... más rutas
  ],
},
];
  1. Manejo de Errores por Ruta Este código usa el Error Boundary en React Router con el hook useRouteError para manejar errores al cargar una ruta. Si ocurre un error en el componente de la página ProductDetail, se muestra un mensaje personalizado con la información del error.
jsx
// pages/ProductDetail.jsx
import { useRouteError } from 'react-router-dom';

function ProductErrorBoundary() {
const error = useRouteError();

return (
  <div>
    <h2>Error al cargar el producto</h2>
    <p>{error.message}</p>
  </div>
);
}
  1. Lazy Loading de Rutas Este código implementa carga perezosa (lazy loading) para el componente Dashboard usando React.lazy. La ruta /dashboard se carga de manera diferida, y mientras se espera, se muestra un mensaje de “Cargando…” gracias al componente Suspense. Esto mejora el rendimiento de la aplicación al cargar los componentes solo cuando son necesarios.
jsx
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

const Dashboard = lazy(() => import('./pages/Dashboard'));

function App() {
return (
  <Routes>
    <Route path="/" element={<RootLayout />}>
      <Route
        path="dashboard"
        element={
          <Suspense fallback={<div>Cargando...</div>}>
            <Dashboard />
          </Suspense>
        }
      />
    </Route>
  </Routes>
);
}