Fecha publicación: 10 nov 2021

React Router 6 - Rutas anidadas

Otro de los cambios que incluye la versión 6 de React Router es el manejo las ruta anidadas.

En este video tutorial vamos a montar un maestro detalle en el que por un lado tendremos un listado de usuarios de Github que pertenecen a una organización y en la misma página (utilizando una ruta anidada), el detalle del usuario que seleccionemos.

Manos a la obra

Partimos de un ejemplo en el que simulamos:

  • Una página de login.
  • Una página de organización en la que:
    • Mostramos los miembros que pertenecen a la organización de Github Lemoncode
    • Y queremos que cuando pinchemos en un miembro mostremos el detalle del este (ahora mismo esta sección la tenemos harcodeada).

A nivel de código:

  • Tenemos el router con las dos páginas.
  • Tenemos una página de organización en la que instanciamos el componente de lista y el de detalle de un miembro.

Vamos a migrarlo a nested route:

  • Vamos a quitar la vista de detalle de la página y decirle que lo que sea lo cargue en el componente outlet que nos ofrece react-router-dom

./src/pages/organization.tsx

import React from "react";
+ import { Outlet } from "react-router-dom";
import { ListComponent, DetailComponent } from "./components";

export const OrganizationPage: React.FC = () => {
  return (
    <>
      <h2>Hello from Organization page</h2>
      <ListComponent />
-      <DetailComponent />
+      <Outlet />
    </>
  );
};

En las rutas vamos a indicarle que dentro de la ruta organization vamos a tener una subruta que tendrá como parámetro el campo login del miembro seleccionado (a este parámetro lo llamaremos slugLogin).

./src/app.tsx

import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { LoginPage, OrganizationPage } from "./pages";

export const App = () => {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<LoginPage />} />
-        <Route path="/organization" element={<OrganizationPage />} />
+        <Route path="/organization" element={<OrganizationPage />} >
+          <Route path=":loginSlug" element={<DetailComponent />} />
+        <Route/>
      </Routes>
    </Router>
  );
};

Es decir, ahí le estamos diciendo que la ruta /organization va a tener una subruta con el login del miembro seleccionado, por ejemplo /organization/brauliodiez.

Ahora nos toca actualizar el listado de usuario y utilizar el Link de react-router para navegar, de primeras podríamos usar algo así como:

./src/pages/components/list.component.tsx

import React from "react";
+ import { Link } from "react-router-dom";
import { getOrganizationMemberCollection } from "../../api";

./src/pages/components/list.component.tsx

  return (
    <>
      <ul>
        {memberCollection.map((member) => (
          <li key={member.login}>
-            {member.login}
+            <Link to={`/organization/${member.login}`}>{member.login}</Link>
          </li>
        ))}
      </ul>
    </>
  );

También podemos generar la url utilizando generatePath de react-router-dom:

./src/pages/components/list.component.tsx

import React from "react";
- import { Link } from "react-router-dom";
+ import { Link, generatePath } from "react-router-dom";

Y cuando construimos el enlace:

./src/pages/components/list.component.tsx

          <li key={member.login}>
-            <Link to={`/organization/${member.login}`}>{member.login}</Link>
+            <Link to={generatePath('/organization/:loginSlug', {loginSlug: member.login})`}>{member.login}</Link>
          </li>

Ojo, la versión 5 te hacía encoding de los parámetros, en la 6 no, si prevés que tus parametros pueden tener "caracteres raros" (por ejemplo una barra /), busca una librería que los codifique y escape correctamente.

Ya tenemos el enlace, nos falta que la vista de detalle lo tenga en cuenta, para ello hacemos uso del hook de react-router-dom useParams:

./src/pages/components/detail.component

import React from "react";
+ import { useParams } from "react-router-dom";
import { getMemberDetail } from "../../api";

./src/pages/components/detail.component

export const DetailComponent = () => {
+  const { loginSlug } = useParams();

  const [member, setMember] = React.useState<MemberDetailEntity>(
    createDefaultMemberDetail()
  );

  React.useEffect(() => {
+    if (loginSlug) {
-      getMemberDetail("brauliodiez").then((memberCollection) => {
+      getMemberDetail(loginSlug).then((memberCollection) => {
+    }
      setMember(memberCollection);
    });
-  }, []);
+  }, [loginSlug]);

  return (
    <>
      <h2>Hello from Detail page</h2>
      <p> id: {member.id}</p>
      <p> login: {member.login}</p>
      <p> name: {member.name}</p>
      <p> company: {member.company}</p>
      <p> bio: {member.bio}</p>
    </>
  );
};

Ahora podemos verlo en acción, vamos pinchando en cada enlace y se actualiza la información de detalle de ese perfil, fíjate que en la barra de navegación se actualiza la url pero el componente lista no se recarga.

El código de este ejemplo puedes pinchando en el enlace: repositorio demo Github

¿Te apuntas a nuestro máster?

Si te ha gustado este ejemplo y tienes ganas de aprender Front End guiado por un grupo de profesionales ¿Por qué no te apuntas a nuestro Máster Front End Online Lemoncode? Tenemos tanto edición de convocatoria con clases en vivo, como edición continua con mentorización, para que puedas ir a tu ritmo y aprender mucho.