Kodokon kodokon.com

React moderno: Server Components, use, useTransition, useOptimistic

Explora los server components, la lectura de promesas con use y las actualizaciones optimistas de React 19.

11 min · 3 preguntas

Abrir esta lección en Kodokon

Los 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.

JSX
// 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>
  );
}
Un server component async consulta la base de datos directamente.

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.

JSX
'use client';

import { useState } from 'react';

export function LikeButton({ initialCount }) {
  const [count, setCount] = useState(initialCount);
  return (
    <button onClick={() => setCount(count + 1)}>
      {count} likes
    </button>
  );
}
La directiva 'use client' delimita la zona interactiva.

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.

JSX
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>
  );
}
use suspende el componente hasta que la promesa se resuelve.

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.

JSX
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.
  });
}
La interfaz reacciona de inmediato, el servidor confirma después.

Prueba de conocimientos

Comprueba que has retenido los puntos clave de esta lección.

  1. ¿Qué no puede hacer un server component?
    • Ser declarado async y esperar una petición
    • Leer la base de datos directamente
    • Usar useState y manejadores de eventos
    • Renderizar client components como hijos
  2. ¿Qué regla de los hooks tiene permitido romper la API use?
    • Ser llamada fuera de un componente
    • Ser llamada dentro de una condición o un bucle
    • Ser llamada tras un return anticipado del módulo
    • Ser llamada dentro de una clase
  3. ¿Qué le ocurre al valor de useOptimistic cuando la acción asíncrona termina?
    • Se convierte en el estado permanente del componente
    • Se descarta y el estado real vuelve a tomar el control
    • Se fusiona con el estado real
    • Se mantiene hasta el siguiente renderizado manual