Lecciones del curso
Introducción al desarrollo web full stack con React Router
Todo sobre rutas con React Router Framework
Recordemos entonces que con React Router Framework tenemos a la mano el archivo routes.ts
, que nos permitirá definir cualquier pattern que queramos hacer coincidir con la URL. Este archivo tiene la siguiente forma.
// ./routes.ts import { route } from "@react-router/dev/routes"; export default [ route("cursos/react-router", "./routes/detail-route.tsx"), // pattern ^ ^ módulo de ruta ]
Observa que se utiliza una función que viene desde @react-router/dev/routes
. Esta función se usa dentro del array que se exporta por default del archivo y recibe dos parámetros. El primero es el pathname o pattern que hará match con la URL del navegador, y el segundo, es el path o dirección del archivo que contiene nuestro módulo de ruta donde programaremos su comportamiento. ✅
👀 Si te habían gustado definir las rutas con convenciones en el nombre del archivo y la carpeta routes, aún puedes emplearlas con
@react-router/fs-routes
.
Módulos de ruta
Cómo ya hemos dicho, los módulos de ruta son los archivos donde definiremos el comportamiento de cada una de nuestras rutas.
route("blog/:slug", "./blogPost.tsx"), // módulo de ruta ^^^^^^^^
Si no estás muy familiarizado con la nomenclatura de JS: un módulo no es más que el nombre que recibe un archivo .js
, .ts
o .tsx
moderno, y que puede exportar funciones que React Router usará.
// Con la función loader cargamos datos al componente export async function loader({ params }) { return await db.post.findUnique({where:{slug:params.slug}}); } // Estos datos están disponibles como un prop export default function Component({loaderData}) { return <> <h1>{loaderData.title}</h1> <MarkDown>{loaderData.body}</MarkDown> </> }
Este es un ejemplo de un módulo de ruta muy simple, que consigue un post de blog desde la base de datos a partir del parámetro slug
que viene en la URL.
El componente, ya en el cliente, tiene acceso a este post de blog a través del prop: loaderData
. Dime, ¿podría ser más simple? 🤯
👀 Los módulos de ruta pueden exportar muchas más funciones útiles para nuestra app y también capturar los errores de forma super elegante, pero hablaremos más afondo de cada una de ellas en las siguientes lecciones. Sé paciente. 😌
Rutas anidadas
Trabajar con rutas anidadas puede ser muy benéfico para no repetir JSX y concentrarse en pequeños componentes, pero, aunque pequeños, al ser módulos de ruta son muy potentes. En React Router es muy fácil crear rutas que son hijas de otras rutas:
import { route, index } from "@react-router/dev/routes"; export default [ // Ruta padre (o madre 👩🏻🍼) route("dashboard", "./routes/dashboard.tsx", [ // rutas hijas (o módulos hijos 👶🏻) index("./routes/home.tsx"), route("orders", "./routes/orders.tsx"), // /dashboard/orders ]), ];
Toma en cuenta que la ruta con el segmento “orders”
hará match con la URL /dashboard/orders
, pues es hija de la ruta dashboard. Misma, que cuando la URL coincide solo con el segmento /dashboard
, se renderizará la ruta hija index. 🪄
👀 La función
index()
solo necesita un parámetro: el path o dirección del módulo:index("./cualquier/folder/home.tsx")
. Ojo, estas rutas no pueden tener hijos. 🫃🏻
Es importante decir que estas rutas hijas son renderizadas por medio del componente <Outlet />
que su padre utilizará en el JSX. Hablaremos más de este componente en un momento. 🪆
Rutas de Layout
Hay veces que queremos que nuestros componentes se rendericen dentro de cierto JSX, como cuando existe un componente Navbar o un menú lateral que queremos reutilizar en distintas rutas o mostrar notificaciones y alertas. 🔔 Pero, no siempre necesitamos el segmento al que el anidamiento nos forzará. Es decir, hay veces que queremos el layout de “dashboard” sin el segmento o palabra “dashboard” en la ruta.
import { route, layout, index, prefix, } from "@react-router/dev/routes"; export default [ layout("./layouts/marketing-layout.tsx", [ index("./routes/home.tsx"), route("subscribe", "./routes/contact.tsx"), ]), layout("./routes/dashboard/layout.tsx", [ route("public/orders", "./routes/dashboard/public_orders.tsx"), ]), ...prefix("dashboard", [ index("./routes/dashboard/dashboard-index.tsx"), layout("./routes/dashboard/layout.tsx", [ route("orders", "./routes/dashboard/orders.tsx"), route("orders/:id", "./routes/dashboard/order-detail.tsx"), ]), ]), ];
En este ejemplo, podemos ver que se pueden usar distintos layouts para distintas regiones de nuestra aplicación web. Además, también tenemos una función más: prefix()
. Prefix, nos evita repetirnos y nos ayuda a agrupar rutas sin añadir un segmento previo. Con Prefix, tenemos toda la libertad de emplear el layout que más nos convenga según la temporada o necesidades de marketing. Podemos pensar este pattern como una alternativa avanzada a la simple anidación. ¡Genial! 👍
👀 No te olvides de añadir el componente
<Outlet />
también a tus componentes Layout:
// Outlet es útil para prefix y para el anidamiento común. import { Outlet } from "react-router"; export default function ProjectLayout() { return ( <article> <nav><Link>Cursos</Link><Link>Blog</Link></nav> <main> <Outlet /> // Aquí se renderizarán las rutas 🧑🧒🧒 </main> </article> ); }
Segmentos dinámicos
Los parámetros en la ruta son una herramienta esencial de este bonito framework. Estos parámetros o variables se definen de una manera muy familiar: empleando los dos puntos o el colon como le dicen los gringos. :id
. 🍑
route("orders/:orderId", "./routes/order-detail.tsx"),
El nombre es: segmentos dinámicos. Y, puedes tener todos los necesarios, todos los que quieras y sin culpas. No como cuando no te dejan comer todos los tamales de la posada. 🫔
route("dashboard/:userId/services/:serviceId", "./service.tsx"),
Estos segmentos dinámicos terminan siendo llaves del objeto params que la función loader
recibe. Esto cumple con las mejores convenciones del desarrollo web.
async function loader({ params }) { // ^ { userId: string; serviceId: string } }
¡Maravilloso! 😃
Segmentos opcionales
Estamos por terminar con todas las opciones que tenemos a la mano en las rutas, pero no podemos irnos sin mencionar una de las herramientas más interesantes, me refiero a los segmentos opcionales.
route(":userId?/cursos", "./cursos-o-mis-cursos.tsx"),
Agregando un ?
al segmento, podemos convertir en opcional un parámetro de la ruta. Así como así. 😳
Y también podemos hacer opcionales segmentos que no son dinámicos:
route("blog/:blogSlug/share?", "./post-reader.tsx");
Esta opción es super interesante, porque nos abre un mar de posibilidades a la hora de ponernos creativos con nuestras rutas. 🌊🧜🏻♂️
Splats
Finalmente, los populares “catchAll” o segmentos de estrella (star segments). Que seguramente has visto en algún otro lugar. Si un segmento termina con un asterisco, hará match o coincidirá con cualquier character incluyendo cualquier otro slash (/
).
route("videos/*", "./videos-proxy.tsx"), // /videos/intro.mp4 o /videos/marketing/bumper.mov
👀
const { "*": splat } = params;
Se puede deconstruir en la función loader asignándole un nombre.
¡Uy! Hemos aprendido un montón sobre las nuevas rutas de React Router ¿no crees? Y nos hemos motivado con su simplificación y lo familiar que resulta su uso. 🤓
Ahora que sabemos cómo definir nuestras rutas, asignarles layout o agruparlas con prefixes, yo creo que es momento de seguir avanzando y aprender todo lo que se puede hacer dentro de un módulo de ruta. 😎
Continuemos, no es momento de detenerse. 🏄🏻♂️