11. React Router
🧭 11.1 Introducción a React Router
Section titled “🧭 11.1 Introducción a React Router”¿Qué es React Router?
Section titled “¿Qué es React Router?”React Router es la librería estándar para manejar navegación en aplicaciones React. Permite crear SPAs (Single Page Applications) con múltiples “páginas” sin recargar el navegador.
| Característica | Descripción |
|---|---|
| Navegación | Sin recargar la página |
| URLs | URLs amigables y compartibles |
| Historial | Botones atrás/adelante funcionan |
| Parámetros | URLs dinámicas con datos |
Instalación
Section titled “Instalación”npm install react-router-domConfiguración básica
Section titled “Configuración básica”// main.jsximport 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>);🛤️ 11.2 Definición de rutas
Section titled “🛤️ 11.2 Definición de rutas”Routes y Route
Section titled “Routes y Route”import { Routes, Route } from 'react-router-dom';
// Componentes de páginafunction Inicio() {return <h1>Página de Inicio</h1>;}
function Acerca() {return <h1>Acerca de Nosotros</h1>;}
function Contacto() {return <h1>Contacto</h1>;}
// App con rutasfunction App() {return ( <Routes> <Route path="/" element={<Inicio />} /> <Route path="/acerca" element={<Acerca />} /> <Route path="/contacto" element={<Contacto />} /> </Routes>);}Ruta 404 (No encontrada)
Section titled “Ruta 404 (No encontrada)”import { Routes, Route } from 'react-router-dom';
function NotFound() {return ( <div> <h1>404 - Página no encontrada</h1> <p>La página que buscas no existe.</p> </div>);}
function App() {return ( <Routes> <Route path="/" element={<Inicio />} /> <Route path="/acerca" element={<Acerca />} /> {/* Ruta comodín para 404 */} <Route path="*" element={<NotFound />} /> </Routes>);}🔗 11.3 Navegación con Link y NavLink
Section titled “🔗 11.3 Navegación con Link y NavLink”Componente Link
Section titled “Componente Link”import { Link } from 'react-router-dom';
function Navegacion() {return ( <nav> {/* Link en lugar de <a href=""> */} <Link to="/">Inicio</Link> <Link to="/acerca">Acerca</Link> <Link to="/contacto">Contacto</Link> </nav>);}
// ❌ NO usar <a href="/acerca"> - recarga la página// ✅ Usar <Link to="/acerca"> - navegación SPAComponente NavLink (con estilos activos)
Section titled “Componente NavLink (con estilos activos)”import { NavLink } from 'react-router-dom';
function Navegacion() {return ( <nav> <NavLink to="/" className={({ isActive }) => isActive ? "activo" : ""} > Inicio </NavLink>
<NavLink to="/acerca" className={({ isActive }) => isActive ? "activo" : ""} > Acerca </NavLink>
<NavLink to="/contacto" style={({ isActive }) => ({ color: isActive ? "red" : "blue", fontWeight: isActive ? "bold" : "normal" })} > Contacto </NavLink> </nav>);}Estructura completa con navegación
Section titled “Estructura completa con navegación”import { Routes, Route, Link } from 'react-router-dom';
function Layout({ children }) {return ( <div> <header> <nav> <Link to="/">Inicio</Link> <Link to="/productos">Productos</Link> <Link to="/contacto">Contacto</Link> </nav> </header>
<main>{children}</main>
<footer> 2024 Mi App</footer> </div>);}
function App() {return ( <Layout> <Routes> <Route path="/" element={<Inicio />} /> <Route path="/productos" element={<Productos />} /> <Route path="/contacto" element={<Contacto />} /> </Routes> </Layout>);}📝 11.4 Parámetros de ruta
Section titled “📝 11.4 Parámetros de ruta”Parámetros dinámicos
Section titled “Parámetros dinámicos”import { Routes, Route, useParams } from 'react-router-dom';
// Componente que usa el parámetrofunction DetalleProducto() {// Obtener parámetro de la URLconst { id } = useParams();
return ( <div> <h1>Producto #{id}</h1> {/* Aquí harías fetch del producto con ese ID */} </div>);}
// Definir ruta con parámetrofunction App() {return ( <Routes> <Route path="/productos" element={<ListaProductos />} /> <Route path="/productos/:id" element={<DetalleProducto />} /> </Routes>);}
// URLs que coinciden:// /productos/1 → id = "1"// /productos/abc → id = "abc"// /productos/123 → id = "123"Múltiples parámetros
Section titled “Múltiples parámetros”import { useParams } from 'react-router-dom';
function ArticuloCategoria() {const { categoria, slug } = useParams();
return ( <div> <p>Categoría: {categoria}</p> <p>Artículo: {slug}</p> </div>);}
// Ruta<Route path="/blog/:categoria/:slug" element={<ArticuloCategoria />} />
// URL: /blog/tecnologia/react-hooks// categoria = "tecnologia"// slug = "react-hooks"Ejemplo práctico con fetch
Section titled “Ejemplo práctico con fetch”import { useState, useEffect } from 'react';import { useParams, Link } from 'react-router-dom';
function DetalleUsuario() {const { id } = useParams();const [usuario, setUsuario] = useState(null);const [loading, setLoading] = useState(true);
useEffect(() => { fetch(`https://jsonplaceholder.typicode.com/users/${id}`) .then(res => res.json()) .then(data => { setUsuario(data); setLoading(false); });}, [id]); // Re-fetch cuando cambia el ID
if (loading) return <p>Cargando...</p>;
return ( <div> <Link to="/usuarios">← Volver</Link> <h1>{usuario.name}</h1> <p>Email: {usuario.email}</p> <p>Teléfono: {usuario.phone}</p> </div>);}🔍 11.5 Query parameters
Section titled “🔍 11.5 Query parameters”useSearchParams
Section titled “useSearchParams”import { useSearchParams } from 'react-router-dom';
function BusquedaProductos() {const [searchParams, setSearchParams] = useSearchParams();
// Leer parámetrosconst query = searchParams.get('q') || '';const categoria = searchParams.get('categoria') || 'todas';const pagina = searchParams.get('pagina') || '1';
// Actualizar parámetrosconst buscar = (termino) => { setSearchParams({ q: termino, categoria, pagina: '1' });};
const cambiarCategoria = (cat) => { setSearchParams({ q: query, categoria: cat, pagina: '1' });};
return ( <div> <input value={query} onChange={e => buscar(e.target.value)} placeholder="Buscar..." />
<select value={categoria} onChange={e => cambiarCategoria(e.target.value)} > <option value="todas">Todas</option> <option value="electronica">Electrónica</option> <option value="ropa">Ropa</option> </select>
<p>Buscando: "{query}" en {categoria}</p> </div>);}
// URL resultante: /productos?q=laptop&categoria=electronica&pagina=1🚀 11.6 Navegación programática
Section titled “🚀 11.6 Navegación programática”useNavigate
Section titled “useNavigate”import { useNavigate } from 'react-router-dom';
function FormularioLogin() {const navigate = useNavigate();
const handleSubmit = async (e) => { e.preventDefault();
// Simular login const exito = await login(email, password);
if (exito) { // Navegar a dashboard navigate('/dashboard');
// O reemplazar historial (no puede volver atrás) // navigate('/dashboard', { replace: true }); }};
return ( <form onSubmit={handleSubmit}> {/* ... campos ... */} <button type="submit">Iniciar Sesión</button> </form>);}Navegar con datos (state)
Section titled “Navegar con datos (state)”import { useNavigate, useLocation } from 'react-router-dom';
// Enviar datosfunction ListaProductos() {const navigate = useNavigate();
const verDetalle = (producto) => { navigate(`/productos/${producto.id}`, { state: { producto } // Pasar datos });};
return ( <ul> {productos.map(p => ( <li key={p.id}> {p.nombre} <button onClick={() => verDetalle(p)}>Ver</button> </li> ))} </ul>);}
// Recibir datosfunction DetalleProducto() {const location = useLocation();const producto = location.state?.producto;
if (!producto) { return <p>Producto no encontrado</p>;}
return <h1>{producto.nombre}</h1>;}Navegar atrás/adelante
Section titled “Navegar atrás/adelante”import { useNavigate } from 'react-router-dom';
function Navegacion() {const navigate = useNavigate();
return ( <div> <button onClick={() => navigate(-1)}>← Atrás</button> <button onClick={() => navigate(1)}>Adelante →</button> <button onClick={() => navigate('/')}>Ir a Inicio</button> </div>);}📂 11.7 Rutas anidadas
Section titled “📂 11.7 Rutas anidadas”Outlet para rutas hijas
Section titled “Outlet para rutas hijas”import { Routes, Route, Outlet, Link } from 'react-router-dom';
// Layout del dashboardfunction DashboardLayout() {return ( <div className="dashboard"> <aside> <nav> <Link to="/dashboard">Resumen</Link> <Link to="/dashboard/perfil">Perfil</Link> <Link to="/dashboard/configuracion">Configuración</Link> </nav> </aside>
<main> {/* Aquí se renderizan las rutas hijas */} <Outlet /> </main> </div>);}
// Componentes hijosfunction DashboardResumen() {return <h1>Resumen del Dashboard</h1>;}
function DashboardPerfil() {return <h1>Mi Perfil</h1>;}
function DashboardConfig() {return <h1>Configuración</h1>;}
// Definir rutas anidadasfunction App() {return ( <Routes> <Route path="/" element={<Inicio />} />
{/* Ruta padre con layout */} <Route path="/dashboard" element={<DashboardLayout />}> {/* Rutas hijas */} <Route index element={<DashboardResumen />} /> <Route path="perfil" element={<DashboardPerfil />} /> <Route path="configuracion" element={<DashboardConfig />} /> </Route> </Routes>);}Rutas protegidas
Section titled “Rutas protegidas”import { Navigate, Outlet } from 'react-router-dom';
// Componente para proteger rutasfunction RutaProtegida({ isAuthenticated }) {if (!isAuthenticated) { // Redirigir a login si no está autenticado return <Navigate to="/login" replace />;}
// Si está autenticado, mostrar contenidoreturn <Outlet />;}
// Uso en las rutasfunction App() {const [isAuthenticated, setIsAuthenticated] = useState(false);
return ( <Routes> <Route path="/" element={<Inicio />} /> <Route path="/login" element={<Login />} />
{/* Rutas protegidas */} <Route element={<RutaProtegida isAuthenticated={isAuthenticated} />}> <Route path="/dashboard" element={<Dashboard />} /> <Route path="/perfil" element={<Perfil />} /> <Route path="/admin" element={<Admin />} /> </Route> </Routes>);}📝 Resumen
Section titled “📝 Resumen”
🐝