React

Todo lo que Necesitas Saber Sobre React Router 6

Samantha Correa
Samantha Correa

Como ya sabrás React nos permite crear SPA (Single Page Application), y mostrar varias vistas dentro de la misma página, sin embargo, tener navegación entre páginas, una URL específica para una vista o simplemente retroceder/avanzar en el historial de navegación son características que todo usuario debería poder hacer en un sitio web, React logra todo esto con ayuda de la librería React Router.

React Router tiene tres paquetes:

  • react-router
  • react-router-native
  • react-router-dom

En este artículo, haremos uso de react-router-dom para crear rutas, manipular el historial y generar rutas anidadas.

La meta es que al finalizar este artículo puedas generar rutas en un proyecto de React utilizando la versión 6 de la librería React Router, además haremos uso de los siguientes hooks: useParams y useNavegate.

Requisitos

Este artículo teórico-práctico asume conocimientos básicos de JavaScript y React, por tanto, es importante:

  • Estar familiarizado con los componentes de React.
  • Tener conocimiento de los siguientes métodos de arreglos: map y find. Si necesitas refrescar tu memoria en este tema te aconsejo el siguiente artículo → La Guía Definitiva de Métodos de Arreglos

¿Qué vamos a desarrollar?

Construiremos una aplicación en donde puedas acceder a un listado de mascotas y podrás navegar a la vista de detalle de cada una de nuestras mascotas de manera dinámica.

Aqui encontraras la version final

Overview

React Router

React Router es la librería más popular para la gestión de rutas en un proyecto de React, esta tiene tres componentes esenciales:

  • <BrowserRouter/> → Conecta nuestra aplicación a la URL del navegador, es decir mantiene la interfaz de usuario en sincronía con la URL del navegador mediante la API History de HTML5.
  • <Routes/> → Genera un árbol de rutas y a partir de este nos permite reemplazar la vista con el componente que coincide con la URL de nuestra barra de navegación y nos va a renderizar solamente dicho componente.
  • <Route/> → Representa una ruta en el árbol, necesita al menos las siguientes propiedades path y element, para representar una ruta.

Creación del ambiente de desarrollo.

Crea una aplicación con React

npx create-react-app router-exercise
cd router-exercise

Posteriormente, necesitamos instalar react-router-dom a nuestro proyecto para hacer uso de sus componentes:

npm install react-router-dom

Configuración del Router

Comencemos a configurar nuestro Router, para eso genera el siguiente archivo dentro de la carpeta src/Router.js este archivo sustituirá al archivo App.js en el index.js de nuestra aplicación, en él haremos la configuración y la gestión de nuestras rutas:

Primero si queremos proveer a toda nuestra aplicación de rutas tenemos que posicionar al componente BrowserRouter como el componente padre de todas nuestras rutas es decir este debe envolverlas de la siguiente manera:

//src > Router.js
import { BrowserRouter, Routes, Route } from "react-router-dom";
const Router = () => {
const Home = () => <h1>Home</h1>;
const Pets = () => <h1>Pet List</h1>;
const Layout = () => <h1>Layout</h1>;
return (
<>
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}></Route>
</Routes>
</BrowserRouter>
</>
);
};
export default Router;

Ahora como habrás notado agregamos el componente Routes antes de nuestra primera ruta, esto se debe a que Routes genera un árbol de rutas a partir de su props.children es decir que cada Route es un elemento hijo de Routes y esto es así, ya que en el momento en que cambiamos la URL en nuestra aplicación, Routes busca en todos sus children Routes para encontrar la mejor coincidencia y renderizar esa rama en la interfaz del usuario. Hasta este momento solo tenemos una rama en nuestro árbol de rutas la cual renderiza el , agreguemos más rutas a nuestra aplicación:

//src > Router.js
import { BrowserRouter, Routes, Route } from "react-router-dom";
const Router = () => {
const Home = () => <h1>Home</h1>;
const Pets = () => <h1>Pet List</h1>;
const Layout = () => <h1>Layout</h1>;
return (
<>
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}></Route>
<Route path="home" element={<Home />}></Route>
<Route path="pets" element={<Pets />}></Route>
</Routes>
</BrowserRouter>
</>
);
};
export default Router;

Dentro de nuestro Router podemos generar todas las rutas que queramos solo necesitamos hacer uso del componente Route agregarle al menos dos propiedades path y element, de esta manera si el path coincide con la URL del navegador renderizara el valor de element.

Perfecto hasta este punto ya tenemos nuestro Router con una configuración básica, podemos dirigirnos a cada una de las rutas que configuramos y obtendremos una vista especifica. Continuemos con el desarrollo de nuestro componente Layout, tener una navbar no solo nos facilitará el desarrollo también provee a nuestros usuarios de los enlaces disponibles dentro de nuestra aplicación, para ello React Router nos proporciona el siguiente componente:

  • <**Link** /> Este componente recibe en su propiedad to el path de la ruta a la cual nos queremos dirigir dentro de nuestra aplicación. Es importante hacer uso de este, ya que además de permitirnos crear los enlaces de navegación este no recarga nuestra aplicación, característica fundamental de una SPA.
import { Link } from "react-router-dom";
const Layout = () => {
return (
<>
<nav style={{ display: "flex", justifyContent: "space-around" }}>
<Link to="/home">Home</Link>
<Link to="/pets">Pets</Link>
</nav>
</>
);
};
export default Layout;

Rutas Anidadas

Las rutas anidadas nos permite tener persistencia de componentes es decir nosotros necesitamos que la navbar persista en todas nuestras vistas para lograr esto, la ruta que renderiza el componente Layout debe envolver a todas las rutas restantes de la siguiente manera:

import { BrowserRouter, Routes, Route } from "react-router-dom";
import Layout from "./components/Layout";
const Router = () => {
const Home = () => <h1>Home</h1>;
const Pets = () => <h1>Pet List</h1>;
// const Layout = () => <h1>Layout</h1>;
return (
<>
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route path="home" element={<Home />}></Route>
<Route path="pets" element={<Pets />}></Route>
</Route>
</Routes>
</BrowserRouter>
</>
);
};
export default Router;

De esta forma Route Layout paso a ser el componente padre de todas las rutas restantes, puedes imaginas esto como bloques de lego, paso a ser la base de las demás rutas, y podemos ir agregando bloques a esta base tanto como queramos, es decir que nuestras rutas hijas podrían seguir anidando y persistiendo en la interfaz del usuario.

Es importante resaltar que este componente Layout no sabe en donde debe renderizar el componente del Route hijo que corresponda con la URL actual, para esto React Router nos proporciona el componente:

Este componente solamente renderiza en el componente padre la siguiente coincidencia en las rutas.

import { Link, Outlet } from "react-router-dom";
const Layout = () => {
return (
<>
<nav style={{ display: "flex", justifyContent: "space-around" }}>
<Link to="/home">Home</Link>
<Link to="/pets">Pets</Link>
</nav>
<Outlet />
</>
);
};
export default Layout;

Ruta index

Seguro notaste que podemos dirigirnos a la ruta “/home“ y a la ruta “/pets” sin embargo, mientras permanecemos en la ruta raíz “/” Outlet no renderiza nada, podemos agregar una ruta hija que se renderice por defecto de la siguiente manera:

import { BrowserRouter, Routes, Route } from "react-router-dom";
import Layout from "./components/Layout";
const Router = () => {
const Home = () => <h1>Home</h1>;
const Pets = () => <h1>Pet List</h1>;
// const Layout = () => <h1>Layout</h1>;
return (
<>
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
**<Route index element={<Home />}></Route>**
<Route path="pets" element={<Pets />}></Route>
</Route>
</Routes>
</BrowserRouter>
</>
);
};
export default Router;

De esta forma cuando estemos posicionados en la ruta “/” Outlet tomara el valor del element de la ruta index para renderizarlo en el componente padre.

Ruta 404

Ahora imagina que nuestro usuario ingresa una URL desconocida… Lo que sucederá es que no se renderizara nada, ya que no existe ninguna ruta correspondiente, podemos configurar una ruta global que se muestre en caso de no haber coincidencias de la siguiente manera:

import { BrowserRouter, Routes, Route } from "react-router-dom";
import Layout from "./components/Layout";
const Router = () => {
const Home = () => <h1>Home</h1>;
const Pets = () => <h1>Pet List</h1>;
// const Layout = () => <h1>Layout</h1>;
return (
<>
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />}></Route>
<Route path="pets" element={<Pets />}></Route>
**<Route path="*" element={<h1>404</h1>}></Route>**
</Route>
</Routes>
</BrowserRouter>
</>
);
};
export default Router;

Rutas con segmentos dinámicos

Tener un segmento dinámico en nuestra ruta significa que este puede coincidir con cualquier valor en dicho segmento por ejemplo la ruta /pets/:id coincidirá con una URL como /pets/1. Pongámoslo en práctica, ahora trabajemos en nuestro componente Pets.js, este componente renderizara una lista con los links para navegar a la vista de detalle de cada mascota, necesitaras toda la información de nuestras mascotas para esto crea dentro de la carpeta src el archivo data.js:

export const pets = [
{
id: 1,
name: "Ally",
image: "https://i.ibb.co/88qydfc/lilu.jpg",
age: 2,
},
{
id: 2,
name: "Molly",
image: "https://i.ibb.co/PcWmskm/molly.jpg",
age: 1,
},
{
id: 3,
name: "Mayumi",
image: "https://i.ibb.co/3pvs9XR/mayumi2.jpg",
age: 7,
},
{
id: 4,
name: "Himeko",
image: "https://i.ibb.co/kMfJ9Dn/himeko.jpg",
age: 4,
},
{
id: 5,
name: "Sirius",
image: "https://i.ibb.co/PT5Bywj/sirius.jpg",
age: 1,
},
{
id: 6,
name: "Askan",
image: "https://i.ibb.co/QK5tW2z/askan.jpg",
age: 6,
},
{
id: 7,
name: "Negrito",
image: "https://i.ibb.co/C8fjGfs/negrito.jpg",
age: 1,
},
];
//src > components > Pets.js
import { Link } from "react-router-dom";
import { pets } from "../data";
const Pets = () => {
return (
<>
<div style={{ textAlign: "center" }}>
<h2>Pets</h2>
{pets.map((e) => (
<p key={e.id}>
<Link to={`${e.id}`}>{e.name}</Link>
</p>
))}
</div>
</>
);
};
export default Pets;

Ahora cuando hacemos clic en el nombre de cada mascota, vemos que la URL cambia de manera dinámica, cada una de estas URL mostrara datos sobre diferentes mascotas por lo que necesitamos una nueva ruta y un nuevo componente que muestre esa información. Cree dentro de la carpeta components el archivo Details.js y dentro del Router.js configuremos la siguiente ruta:

import { BrowserRouter, Routes, Route } from "react-router-dom";
import Layout from "./components/Layout";
import Pets from "./components/Pets";
const Router = () => {
const Home = () => <h1>Home</h1>;
// const Pets = () => <h1>Pet List</h1>;
// const Layout = () => <h1>Layout</h1>;
return (
<>
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />}></Route>
<Route path="pets" element={<Pets />}></Route>
**<Route path="pets/:id" element={<h1>detalles</h1>}></Route>**
<Route path="*" element={<h1>404</h1>}></Route>
</Route>
</Routes>
</BrowserRouter>
</>
);
};
export default Router;

Details.js necesita dos cosas para renderizar de manera dinámica el contenido de una sola mascota primero necesita conocer el valor que toma :id en la ruta actual por lo que haremos uso de useParams y después necesita identificar de todo el listado de mascotas la mascota actual:

import { useParams } from "react-router-dom";
import { pets } from "../data";
const Details = () => {
const { id } = useParams();
const navigate = useNavigate();
const onePet = pets.find((e) => e.id === Number(id));
return (
<>
<div
style={{
textAlign: "center",
}}
>
<h1>{onePet.name}</h1>
<h2>{onePet.description}</h2>
<img src={onePet.image} alt={onePet.name} width={100} />
</div>
</>
);
};
export default Details;

useParams - Este hook devuelve un objeto con las propiedades y el valor de los segmentos dinámicos de la URL.

Finalmente, podemos hacer uso de nuestro hook useNavigate el cual nos devuelve una función que nos permite navegar programáticamente dentro de nuestra app:

import { useParams, useNavigate } from "react-router-dom";
import { pets } from "../data";
const Details = () => {
const { id } = useParams();
const navigate = useNavigate();
const onePet = pets.find((e) => e.id === Number(id));
return (
<>
<div
style={{
textAlign: "center",
}}
>
<h1>{`Nombre: ${onePet.name}`}</h1>
<h2>{`Edad: ${onePet.age}`}</h2>
<img src={onePet.image} alt={onePet.name} width={300} />
</div>
<button onClick={() => navigate(-1)}>← Back</button>
</>
);
};
export default Details;

useNavigate al igual que el valor que le pasamos a la propiedad to del componente Link recibe la ruta que especifica a donde queremos que nuestro usuario navegue, pero también puede recibir el valor delta (número) al que desea ir en el historial. Por ejemplo, en este caso navigate(-1) equivale a presionar el botón Atrás. Por último no olvides modificar tu Router.js para que renderice en nuestra ruta “pets/:id”, este componente Details.js

Conclusiones

Con este artículo, configuramos nuestro Router.js haciendo uso de rutas anidadas y las mejores características de la versión 6 de React Router, así como el manejo de rutas con segmentos dinámicos empleando useParams y useNavigate. Te invito a integrar otra ruta anidada dentro de la ruta pets y revisar el repositorio terminado.

Artículos Relacionados

¿Quieres mejorar tus habilidades de frontend?