Saltar al contenido

Clase 36 - Next JS

#react #next

🤔 - ¿Qué es Next.js?

Next.js es un framework de React de código abierto desarrollado por Vercel que proporciona capacidades de renderizado del lado del servidor (SSR), generación de sitios estáticos (SSG), y muchas otras características avanzadas. A diferencia de React puro, Next.js ofrece una solución completa para aplicaciones web de producción con optimizaciones automáticas y un sistema de routing basado en archivos.

💫 - Ventajas Principales

  • Renderizado Híbrido: Soporte para SSG, SSR, ISR y CSR según las necesidades de cada página.
  • Routing Automático: Sistema de routing basado en la estructura de archivos.
  • Optimización Automática: Code splitting, lazy loading, optimización de imágenes.
  • TypeScript: Soporte nativo y configuración automática.
  • Performance: Web Vitals optimizados, pre-fetching inteligente.
  • Developer Experience: Hot reloading, error reporting mejorado.
  • Deploy Simple: Integración nativa con Vercel y otras plataformas.

🏗️ - Modos de Renderizado en Next.js

Antes de profundizar en Next.js, es crucial entender los diferentes modos de renderizado disponibles, ya que esta comprensión guiará nuestras decisiones arquitectónicas.

🔄 - Client-Side Rendering (CSR)

Definición: El contenido se renderiza completamente en el navegador usando JavaScript.

"use client"
import { useState, useEffect } from 'react';
interface User {
id: number;
name: string;
email: string;
}
export default function Profile() {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
// Los datos se cargan después del montaje del componente
fetch('/api/user')
.then(res => res.json())
.then((data: User) => {
setUser(data);
setLoading(false);
});
}, []);
if (loading) return <div>Cargando...</div>;
return (
<div>
<h1>Perfil de {user?.name}</h1>
<p>Email: {user?.email}</p>
</div>
);
}

🖥️ - Server-Side Rendering (SSR)

Definición: El HTML se genera en el servidor para cada request, enviándose completamente renderizado al cliente.

interface Post {
id: number
title: string
body: string
}
interface Comment {
id: number
name: string
body: string
}
export default async function Page () {
const id = 10
let post: Post | null = null
let comments: Comment[] = []
let errorMsg = ""
try {
const data = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, { cache: "no-store" })
if (!data.ok) throw new Error("No se pudo cargar el post")
post = await data.json()
const commentsData = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}/comments`, { cache: "no-store" })
if (!commentsData.ok) throw new Error("No se pudo cargar los comentarios")
comments = await commentsData.json()
} catch (error: any) {
errorMsg = error?.message || "Error desconocido"
}
if (errorMsg) {
return <div className="p-6 text-red-600">Error: {errorMsg}</div>
}
return (
<article className="p-6">
<h1 className="text-2xl font-bold">{post?.title}</h1>
<p className="mt-2">{post?.body}</p>
<section className="mt-6">
<h3 className="text-xl font-semibold">
Comentarios ({comments.length})
</h3>
<div className="mt-2 space-y-2">
{comments.map((comment) => (
<div key={comment.id} className="border-b pb-2">
<strong>{comment.name}</strong>: {comment.body}
</div>
))}
</div>
</section>
</article>
)
}

📄 - Static Site Generation (SSG)

Definición: Las páginas se generan como HTML estático en tiempo de build, sirviendo archivos pre-renderizados.

type Post = {
id: number
title: string
body: string
}
async function getPosts () {
const res = await fetch('https://jsonplaceholder.typicode.com/posts')
return res.json()
}
export default async function Page () {
const posts = await getPosts()
return posts.map((post: Post) => (
<article key={post.id}>
<h1>{post.title}</h1>
<p>{post.body}</p>
</article>
))
}

🔄 - Incremental Static Regeneration (ISR)

Definición: Combina los beneficios de SSG con la capacidad de regenerar páginas estáticas bajo demanda.

type Post = {
id: number
title: string
body: string
}
// NEXT invalidará la caché de la página cada 60 segundos
export const revalidate = 60
export async function generateStaticParams () {
const posts = await getPosts()
return posts.map((post: Post) => ({ id: post.id.toString() }))
}
async function getPosts () {
const res = await fetch('https://jsonplaceholder.typicode.com/posts')
return res.json()
}
export default async function Page ({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
const post: Post = await response.json()
return (
<article>
<h1>{post.title}</h1>
<p>{post.body}</p>
</article>
)
}

⚡ - React Server Components (RSC)

Definición: Componentes que se ejecutan en el servidor, reduciendo el bundle de JavaScript enviado al cliente.

import { Suspense } from 'react'
interface UserData {
id: number
name: string
username: string
email: string
address: {
street: string
suite: string
city: string
zipcode: string
geo: {
lat: string
lng: string
}
}
phone: string
website: string
company: {
name: string
catchPhrase: string
bs: string
}
}
// Este componente se ejecuta en el servidor
async function ServerData () {
// Fetch directo en el componente, sin useEffect
const userData: UserData = await fetch('https://jsonplaceholder.typicode.com/users/1', {
next: { revalidate: 300 } // Cache por 5 minutos
}).then(r => r.json())
return <UserStats data={userData} />
}
export default function Dashboard () {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<div>Cargando estadísticas...</div>}>
<ServerData />
</Suspense>
<Suspense fallback={<div>Cargando actividad...</div>}>
<RecentActivity />
</Suspense>
</div>
)
}
// -- Componentes -------------------------------------------------------------
function UserStats ({ data }: { data: UserData }) {
return (
<div>
<h2>Información del usuario</h2>
<p><strong>Nombre:</strong> {data.name}</p>
<p><strong>Usuario:</strong> {data.username}</p>
<p><strong>Email:</strong> {data.email}</p>
<p><strong>Teléfono:</strong> {data.phone}</p>
<p><strong>Ciudad:</strong> {data.address.city}</p>
<p><strong>Compañía:</strong> {data.company.name}</p>
</div>
)
}
function RecentActivity () {
return (
<div>
<h2>Actividad reciente</h2>
<p>Esta información se cargará en segundo plano</p>
</div>
)
}

🗺️ - Sistema de Routing

Next.js incluye dos sistemas de routing: Pages Router (legacy) y App Router (recomendado).

📁 - App Router (app/)

El App Router utiliza la estructura de carpetas para definir rutas:

  • Directorioapp/
    • page.ts
    • Directorioabout/
      • page.ts
    • Directorioblog/
      • page.ts
      • Directorio[slug]/
        • page.ts
    • Directorioproducts/
      • page.ts
      • Directorio[id]/
        • page.ts
        • Directorioreviews/
          • page.ts
    • Directoriodashboard/
      • layout.ts
      • page.ts
      • Directoriosettings/
        • page.ts
      • Directorioanalytics/
        • page.ts

📚 - Documentación de Next