NextSteps
React Next.js Arquitectura Frontend

Eligiendo una arquitectura de React: SPA, Frameworks y el futuro de la obtención de datos

Martin Rojas
7 min de lectura
Eligiendo una arquitectura de React: SPA, Frameworks y el futuro de la obtención de datos

Aprende cómo las arquitecturas basadas en SPA y frameworks en React impactan la obtención de datos. Explora herramientas modernas como cargadores de React Router y React Server Components.

Las estrategias de obtención de datos (data-fetching) en React se han convertido en un tema candente, y con razón. Están estrechamente ligadas a la arquitectura de tu aplicación, lo que significa que la decisión de construir una Aplicación de Una Sola Página (SPA) o usar un framework como Next.js o Remix a menudo dicta tu enfoque para obtener datos. De hecho, la propia documentación del equipo de React sugiere usar frameworks para la mayoría de los proyectos, y existen razones de peso para ello.

Este post explora por qué la elección entre una SPA y un framework importa tanto, las compensaciones entre la obtención de datos del lado del cliente y del lado del servidor, y cómo herramientas modernas como React Router, los cargadores (loaders) y los React Server Components (RSC) están cambiando el panorama del desarrollo con React.


¿Deberías usar un framework?

Los frameworks de React como Next.js y Remix suelen verse como híbridos que mezclan los mejores aspectos de las SPA y las aplicaciones renderizadas en el servidor (MPA). Su objetivo es evitar las trampas de las SPA, como las cascadas de red (network waterfalls) y la falta de precarga, manteniendo al mismo tiempo su interactividad y navegación fluida.

El equipo de React es claro en su recomendación:

“Si estás construyendo una nueva aplicación o un sitio completamente con React, recomendamos usar un framework.”
Documentación de React

Sin un framework, las SPA dependen de estrategias de obtención de datos del lado del cliente —a menudo usando hooks como useEffect— que el equipo de React ya no recomienda. Analicemos por qué.


¿Qué hay de malo con la obtención de datos del lado del cliente?

Las SPA popularizaron el enfoque de obtener datos en el cliente, a menudo activado por el useEffect de React. Sin embargo, existen varios inconvenientes en este método, como señala el equipo de React:

1. Los Efectos no se ejecutan en el servidor

Obtener datos con useEffect es inherentemente del lado del cliente. Esto evita que las SPA aprovechen las capacidades del lado del servidor, como el pre-renderizado o tiempos de respuesta más rápidos para las cargas de datos iniciales.

2. Cascadas de red (Network Waterfalls)

Obtener datos directamente dentro de los componentes puede llevar a “cascadas”. Por ejemplo, un componente padre obtiene datos, se renderiza, y solo entonces los componentes hijos obtienen sus datos. Esta obtención secuencial retrasa el renderizado y crea un patrón de escalones en tu pestaña de red:

GET /products/1     ▓▓▓▓▓▓▓
GET /brands          ▓▓▓▓▓▓
GET /products/related  ▓▓▓▓▓▓

Los frameworks mitigan esto obteniendo todos los datos necesarios en paralelo, incluso antes del renderizado.

3. Sin precarga ni caché

Con useEffect, no puedes precargar datos antes de que un componente se renderice, ni puedes cachear respuestas de manera eficiente sin un esfuerzo extra. En contraste, herramientas como TanStack Query y los cargadores de React Router proporcionan capacidades robustas de caché y precarga.

4. Desafíos ergonómicos

Usar useEffect para la obtención de datos requiere gestionar manualmente las condiciones de carrera (race conditions), la lógica de limpieza y los casos borde. Los frameworks y las herramientas de terceros simplifican estas tareas con APIs dedicadas.


Alternativas a useEffect

Si useEffect no es lo ideal, ¿qué deberías usar en su lugar? El equipo de React ofrece dos recomendaciones principales:

  1. Usa un framework como Next.js o Remix.
  2. Para las SPA, usa herramientas como los cargadores de React Router o TanStack Query.

Uso de los cargadores de React Router (Loaders)

Las funciones loader de React Router obtienen datos fuera de los componentes de React. Esto evita las cascadas al paralelizar la obtención de datos para todas las rutas coincidentes.

Ejemplo: Obtención de datos con Loaders

Así es como puedes configurar un cargador en React Router:

import { loader as productLoader, ProductPage } from '~/ProductPage';

const router = createBrowserRouter(
	createRoutesFromElements(
		<Route path="/" element={<RootLayout />}>
			<Route
				path="products"
				loader={productLoader}
				element={<ProductsSubLayout />}>
				<Route
					path=":productId"
					loader={productLoader}
					element={<ProductPage />}
				/>
			</Route>
		</Route>
	)
);

export async function loader({ params }) {
	const product = await fetch(`/api/products/${params.productId}`).then((res) =>
		res.json()
	);
	return product;
}

export function ProductPage() {
	const product = useLoaderData(); // Acceder al resultado del loader
	return <div>{product.name}</div>;
}

Este patrón evita las trampas de useEffect, asegurando que todos los datos necesarios se carguen antes del renderizado.

Loaders + TanStack Query

Para combinar los cargadores de React Router con el poder de caché de TanStack Query, puedes usar queryClient.ensureQueryData:

export async function loader({ params }) {
	return queryClient.ensureQueryData({
		queryKey: ['product', params.productId],
		queryFn: () =>
			fetch(`/api/products/${params.productId}`).then((res) => res.json()),
		staleTime: 1000 * 30, // Caché por 30 segundos
	});
}

export function ProductPage() {
	const product = useLoaderData();
	return <div>{product.name}</div>;
}

Este enfoque te da lo mejor de ambos mundos: caché robusto y sin cascadas.


Obtención de datos en el servidor: Frameworks y React Server Components

La obtención de datos del lado del servidor ha ganado terreno gracias a frameworks como Next.js y Remix, que obtienen los datos antes de enviar el HTML al cliente. Esto reduce los tiempos de carga y elimina la necesidad de obtención iniciada por el cliente en la carga inicial de la página.

Obtención de datos específica del Framework

  • Next.js: Obtiene datos con getServerSideProps o getStaticProps.
  • Remix: Usa funciones loader, similares a React Router.

Estos enfoques aseguran que los datos estén listos cuando la página se carga, evitando los inconvenientes de la obtención del lado del cliente.

React Server Components (RSC)

Los RSC son una funcionalidad más reciente de React diseñada para minimizar el JavaScript del lado del cliente al renderizar componentes completamente en el servidor. A diferencia de los componentes tradicionales renderizados en el servidor, los RSC no se rehidratan en el cliente, lo que significa que no pueden manejar eventos ni estado.

Ejemplo: Límite entre RSC y Componente de Cliente

async function ProductsServerComponent() {
	const products = await fetchProducts();
	return products.map((product) => (
		<ProductClientComponent key={product.id} product={product} />
	));
}

function ProductClientComponent({ product }) {
	return <button>{product.name}</button>; // Componente interactivo del lado del cliente
}

Los RSC son ideales para contenido estático o componentes pesados en datos, mientras que los componentes de cliente manejan la interactividad.


¿Qué arquitectura deberías elegir?

  • SPA: Adecuada para aplicaciones simples e interactivas. Usa herramientas como los cargadores de React Router o TanStack Query para mejorar la ergonomía de la obtención de datos.
  • Framework: Lo mejor para aplicaciones modernas con necesidades complejas de obtención de datos. Los frameworks proporcionan soluciones listas para usar para la obtención y renderizado del lado del servidor.
  • Híbrida: Frameworks como Next.js y Remix te permiten mezclar componentes de servidor y de cliente, dándote flexibilidad.

Conclusión

Tu elección de arquitectura de React —SPA o framework— dicta tu estrategia de obtención de datos. Aunque cada vez se recomiendan más los frameworks para nuevos proyectos, las SPA siguen siendo viables con herramientas como los cargadores y las bibliotecas de caché.

Entender las compensaciones entre la obtención de datos del lado del cliente y del lado del servidor te ayudará a tomar decisiones informadas mientras construyes aplicaciones de React escalables, rápidas y fáciles de mantener.

¿Listo para profundizar? ¡Comparte tus experiencias o preguntas en los comentarios!