Explora los server components, la lectura de promesas con use y las actualizaciones optimistas de React 19.
Abrir esta lección en KodokonLos React Server Components (RSC) redibujan la frontera cliente-servidor. Un server component se ejecuta solo en el servidor: su código nunca llega al bundle del cliente, puede ser async, leer la base de datos o el sistema de archivos directamente, y su salida se envía al navegador como un stream serializado (el payload RSC) que React fusiona con el árbol del cliente. A cambio, no tiene estado, ni efectos, ni acceso al navegador: nada de useState, nada de useEffect, ningún manejador de eventos. Nunca vuelve a renderizarse en el cliente.
// Server component: never sent to the client.
export default async function NotesPage() {
const notes = await db.notes.findAll();
return (
<ul>
{notes.map((note) => (
<li key={note.id}>{note.title}</li>
))}
</ul>
);
}La interactividad vive en los client components, marcados por la directiva 'use client' al principio del archivo. Esta directiva no marca "un client component" sino un límite: todo lo que ese módulo importa también pasa al lado del cliente. Las props que cruzan el límite deben ser serializables: objetos planos, arrays, promesas... pero nunca funciones ordinarias ni instancias de clase.
'use client';
import { useState } from 'react';
export function LikeButton({ initialCount }) {
const [count, setCount] = useState(initialCount);
return (
<button onClick={() => setCount(count + 1)}>
{count} likes
</button>
);
}React 19 introduce use, una API que lee el valor de una promesa o de un contexto. A diferencia de los hooks, use puede llamarse dentro de una condición o de un bucle. Ante una promesa, suspende el componente hasta su resolución, apoyándose en el límite Suspense más cercano. El patrón idiomático: un server component crea la promesa sin esperarla, la pasa como prop, y un client component la consume con use: el servidor comienza a transmitir la página sin esperar a los datos.
import { use, Suspense } from 'react';
function Comments({ commentsPromise }) {
const comments = use(commentsPromise);
return comments.map((c) => (
<p key={c.id}>{c.text}</p>
));
}
function Page({ commentsPromise }) {
return (
<Suspense fallback={<Spinner />}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}Del lado de la interacción, useTransition marca una actualización como no urgente y expone isPending para que puedas mostrar un indicador discreto sin bloquear la entrada. useOptimistic completa el dúo para las mutaciones de red: muestras inmediatamente el estado esperado mientras se ejecuta una acción asíncrona; cuando la acción termina (éxito o fracaso), React descarta el valor optimista y muestra de nuevo el estado real, el de la fuente de verdad.
const [optimisticLikes, addOptimisticLike] =
useOptimistic(likes, (count, delta) => count + delta);
function handleLike() {
startTransition(async () => {
addOptimisticLike(1);
await saveLike();
// At the end, React shows the real state again.
});
}use?useOptimistic cuando la acción asíncrona termina?