Lecciones del curso
Introducción al desarrollo web full stack con React Router
¿Cómo sustituir un useEffect?
La función loader
de React Router es una de las herramientas más útiles del framework; nos ayuda a cargar datos desde la base de datos, pues esta función se ejecuta en el servidor. Pero, no es la única función que podemos usar para conseguir datos; también tenemos a la mano al clientLoader
que, como su nombre lo sugiere, se ejecuta en el cliente. ✅
Con clientLoader
podemos sustituir al useEffect
Si necesitamos cargar datos desde el cliente, puede que rápido pensemos en usar un useEffect
, pues useEffect se ejecuta solo en el cliente. Pero, React Router nos provee de una herramienta mucho mejor que puede correr en paralelo junto con el render. ✌🏼
export const clientLoader = async ({params}) => { const response = await fetch(`/api/users/${params.userId}`); return await res.json(); }
De esta forma, podemos conseguir datos usando los endpoints de nuestras rutas de recursos o cualquier otro servicio o API . 🤯
¿Dónde se coloca lo conseguido por el clientLoader?
Con Remix teníamos el hook: useLoaderData()
, pero ahora, en React Router, podemos acceder a estos datos directamente en los props de la ruta:
export default function Route({loaderData}) { const { displayName, email, photoURL } = loaderData; return ( <> <img src={photoURL} alt="avatar" /> <h1>{displayName}</h1> <p>{email}</p> </> ); }
Así, nuestra app renderizará instantáneamente; mientras en paralelo, consigue los datos que necesita. ¡Y todo desde el cliente!
👀 Si queremos tipado seguro de extremo a extremo (end to end type safety), podemos tipar los props así:
({loaderData}: Route.ComponentProps)
. Hablaremos más de esta utilidad de tipos en la siguiente lección. 🤖
Carga de datos en modo estático
React Router incluye nuevas habilidades para generar contenido estático (SSG). Esto es, que puede crear páginas o archivos HTML que se crean al momento de hacer el build de producción. Muy similar a lo que hace Astro o Hugo; pero con React Router, podemos cargar los datos al momento de crear el build
. 🙀
// react-router.config.ts export default { async prerender() { return ["/users/blissmo", "/blog/static-welcome"] }, }
Todas las rutas que agreguemos en este array que devuelve la función prerender
del archivo de configuración, serán rutas estáticas. Mientras que el resto de nuestras rutas renderizarán de forma usual. ¡Lo cuál es super útil! 🤩
Esto es perfecto, por ejemplo, para un blog. 📑
Podemos combinar todas estas herramientas
Lo mejor es que todas estas opciones que React Router nos ofrece ahora: se pueden combinar. Así, un loader
puede cargar datos para pre-renderizar, pero aún podemos usar clientLoader
para conseguir más datos, una vez en el cliente.
import { db } from "../db"; export async function loader({ params }) { return db.posts.findMany({ where: { author: params.authorId }, take: 4, select:{ slug: true, title: true }, orderBy: { createdAt: 'desc' } }) } export async function clientLoader({params}) { const url = `/api/posts?author=${params.authorId}&take=100&skip=4` return fetch(url).then(res=>res.json()) } export default function Product({loaderData: posts}) { return ( <> {posts.map(post=><PostCard post={post} key={post.id} />)} </> ); }
En este ejemplo, debes notar que tenemos los dos tipos de loaders trabajando juntos. El loader
, en el servidor, carga solo los primeros 4 posts. Estos posts podrían pre-renderizarse. Pero, además, el clientLoader
puede conseguir muchos más una vez que la app está corriendo en el cliente; lo que convierte nuestra página web en un sitio pre-renderizado y SPA al mismo tiempo. ¡Una locura! 😲 Y lo mejor: es que todo esto se puede poner en la cache tanto en el cliente como en el servidor. 👽
¿Genial no? Con todo esto: ¿quién podría extrañar a Next? 😜