Inicio / Artículos / Primitivas de SSR en React 19.2: Explicación de Activity, cacheSignal y PPR

Primitivas de SSR en React 19.2: Explicación de Activity, cacheSignal y PPR

Aprenda cómo el nuevo componente Activity, cacheSignal y el pre-renderizado parcial de React 19.2 brindan a los desarrolladores un control directo sobre el rendimiento del renderizado en servidor.

2240 palabras

El trabajo relacionado con el rendimiento en React suele dividirse en dos categorías: hacer que la renderización inicial en el servidor sea más rápida y eficiente, y lograr que la aplicación del lado del cliente evite renderizaciones innecesarias una vez está montada. Las dos partes de este artículo abordan el problema desde extremos opuestos de ese espectro, pero comparten la misma filosofía subyacente: la velocidad proviene de indicarle a React con precisión qué tareas son importantes, cuáles pueden posponerse y cuáles nunca deben ejecutarse, en lugar de recurrir a más caché o más hardware para resolver el problema. La primera parte trata sobre la renderización en servidor y las nuevas primitivas incluidas en React 19.2; la segunda se centra en las técnicas cotidianas del lado del cliente que mantienen una aplicación montada receptiva.

Reconsiderando el rendimiento de la renderización en servidor en React 19.2

La mayor parte de las guías sobre el “rendimiento SSR” se reducen a acumular capas de caché y confiar en lo mejor. En cambio, React 19.2, lanzado en octubre de 2025, ofrece primitivas dedicadas para controlar directamente el trabajo del servidor. Tratar esta versión como un parche menor implica perderse beneficios reales de velocidad; los detalles que se presentan a continuación son los que realmente marcan la diferencia.

Mantén los componentes activos en lugar de destruirlos

Un problema recurrente en las aplicaciones SSR es que los cambios de pestaña, la apertura de modales y las transiciones de ruta destruyen por completo los componentes, eliminando su estado e obligando a recuperar los datos desde cero. El nuevo componente Activity fue creado específicamente para resolver este problema.

// old way — full unmount, state gone, effects re-run
{activeTab === 'analytics' && <AnalyticsPanel />}

// React 19.2 — stays alive, just deprioritized
<Activity mode={activeTab === 'analytics' ? 'visible' : 'hidden'}>
  <AnalyticsPanel />
</Activity>

Al mantener contenido oculto montado en lugar de destruirlo, React puede cargar por adelantado lo que hay dentro de un bloque Activity oculto antes de que el usuario haga clic en él. Este enfoque se considera capaz de reducir significativamente la latencia percibida en las interfaces de control con un procesamiento server-side intensivo, ya que no hay necesidad de volver a cargar datos ni cambios en el diseño cuando el contenido se vuelve visible.

Deje que cacheSignal limpie automáticamente el trabajo abandonado

Antes de la versión 19.2, si el procesamiento en servidor se interrumpía a mitad de camino —por ejemplo, porque el cliente cambiaba de página o una solicitud se agotaba— las solicitudes en curso y los datos almacenados en caché no tenían forma de saber que ya no eran necesarios. cacheSignal soluciona esto al proporcionar a los Componentes del Servidor de React una señal real de ciclo de vida a la que pueden acceder para realizar la limpieza.

async function getUserOrders(userId, { signal }) {
  const res = await fetch(`/api/orders/${userId}`, { signal });
  return res.json();
}

Cuando se agota el tiempo de vida de la caché, la señal asociada dispara una interrupción que evita que las solicitudes huérfanas sigan consumiendo recursos del procesador en el servidor durante picos de tráfico.

Construir una carcasa estática y transmitir el resto por streaming

El preprocesamiento parcial (PPR) es la principal función de SSR en esta versión. La idea consiste en crear una vez la carcasa estática de una página —navegación, diseño, pie de página—, servirla directamente desde un CDN periférico y transmitir por streaming las partes dinámicas dentro de los límites de Suspense.

<Suspense fallback={<ProductSkeleton />}>
  <PartialPreRender>
    <PersonalizedRecommendations userId={user.id} />
  </PartialPreRender>
</Suspense>

Procesar varios revelados de Suspense juntos

Anteriormente, cuando varios límites de Suspense se resolvían aproximadamente al mismo tiempo, la interfaz de usuario podía sufrir un efecto similar al “popcorn”, con fragmentos de contenido apareciendo uno tras otro en lugar de juntos. React 19.2 agrupa estas actualizaciones para que el comportamiento del cliente y del servidor sea consistente, y también añade soporte para Web Streams en Node.js para equipos que necesitan un control más detallado sobre la transmisión de datos.

Estas API elevan los estándares, no los bajan

Nada de esto sustituye lo básico. Todavía es necesario eliminar los patrones de obtención de datos N+1 y dividir los paquetes monolíticos antes de que estas funcionalidades puedan ayudarle en algo. React 19.2 no disminuye la cantidad mínima de trabajo de optimización requerido; más bien eleva el límite máximo de velocidad que puede alcanzar una aplicación bien optimizada. También vale la pena leer las notas oficiales de lanzamiento de React 19.2, junto con los valores predeterminados de Turbopack introducidos en Next.js 16, que combinan bien con PPR.

A partir de 2026, el rendimiento del SSR no se trata de cachear de manera más agresiva, sino de indicarle a React qué puede esperar, qué puede transmitirse en flujo y qué puede fallar de forma elegante. React 19.2 es lo que finalmente le proporciona el vocabulario para expresar eso.

Más allá de estos mecanismos específicos de SSR, gran parte de lo que hace que una aplicación React parezca rápida se debe a hábitos cotidianos que siguen siendo válidos independientemente de la estrategia de renderizado o de la versión de React. Mientras que la sección anterior se centró en el streaming, el prerendering y Suspense en el lado del servidor, lo que sigue aborda los patrones del lado del cliente que mantienen a cualquier aplicación React receptiva en la práctica.

Técnicas prácticas para el rendimiento diario de React

React ya se renderiza de manera eficiente por defecto. A medida que la aplicación crece, los re-renderizos innecesarios, las listas excesivamente grandes, el JavaScript en exceso y demasiadas llamadas a la red comienzan a acumularse. La solución rara vez implica trucos exóticos: unos pocos hábitos disciplinados suelen ser suficientes para lograrlo.

Renderiza solo lo que realmente necesita actualizarse

Cada recálculo fuerza a React a ejecutar nuevamente el cuerpo de la función de un componente. Eso en sí no es un problema; el verdadero costo radica en repetir tareas costosas cuando en realidad no ha cambiado nada significativo. Evite incluir estado no relacionado dentro de un componente que renderiza una gran parte de su interfaz de usuario, ya que actualizar ese estado obliga a que todo el subárbol se recalcule junto con él. En su lugar, divida los componentes para que una actualización solo afecte a la parte de la interfaz que realmente debe modificarse. El objetivo no es lograr cero recálculos, sino eliminar aquellos que son innecesarios.

Coloque el estado donde realmente se necesite

Resista la tentación de llevar todo el estado hasta la parte superior del árbol de componentes. Si solo un componente lee un valor determinado, allí es exactamente donde debe estar.

function SearchBox() {
  const [query, setQuery] = useState("");

  return (
    <input
      value={query}
      onChange={(e) => setQuery(e.target.value)}
    />
  );
}

Mantener el estado de forma local de esta manera limita la propagación de las actualizaciones, lo que reduce las recargas accidentales en otras partes del árbol. Como regla general, coloque el estado cerca del componente que lo utiliza en lugar de más arriba en la jerarquía.

Derive los valores en lugar de almacenarlos

No todo debe ir en useState. Si ya está registrando firstName y lastName, no hay razón para almacenar también fullName por separado. La versión ineficiente se ve así:

const [fullName, setFullName] = useState("");
useEffect(() => {
  setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);

Un enfoque más simple consiste simplemente en calcular el valor mientras se renderiza:

const fullName = `${firstName} ${lastName}`;

Esto elimina tanto una variable de estado adicional como un Effect innecesario. En general, si un valor puede calcularse en tiempo real durante el renderizado, probablemente no necesite ser estado.

Gestionar listas grandes sin sobrecargar el DOM

Renderizar miles de nodos al mismo tiempo se vuelve costoso rápidamente. Imagina una pantalla de chat con 10,000 mensajes: no es necesario que todos existan simultáneamente en el DOM. Para colecciones grandes, opta por una de las siguientes soluciones:

  • Virtualización
  • Paginación
  • Desplazamiento infinito

La virtualización mantiene solo las filas que se encuentran actualmente a la vista, más un pequeño buffer, en cada momento dado; bibliotecas como react-window implementan este patrón por ti. No obstante, no apliques la virtualización de forma automática: una lista con 50 elementos casi con certeza no la necesita.

Posponer la carga del código hasta que sea necesario

Los usuarios no deberían tener que descargar JavaScript para funcionalidades que aún no han abierto. Las API lazy y Suspense de React te permiten separar ese código del paquete inicial:

const Settings = lazy(() => import("./Settings"));

Con esto en vigor, el componente Settings se carga solo una vez que realmente se renderiza, en lugar de incluirse con la carga inicial de la página. Esto es útil para funciones pesadas o poco utilizadas: gráficos, editores, mapas, pantallas de configuración, paneles grandes, y generalmente permite una carga inicial más rápida.

Mantener la interfaz interactiva receptiva bajo carga

No todas las actualizaciones necesitan realizarse al mismo tiempo que la entrada del usuario. Un cuadro de búsqueda debe reaccionar instantáneamente a las teclas pulsadas, incluso mientras el filtrado de un gran conjunto de datos se ejecuta en segundo plano con menor prioridad. Los hooks useTransition y useDeferredValue de React están diseñados precisamente para este tipo de equilibrio. El amortiguamiento de la entrada bruta ofrece un efecto similar:

const debouncedSearch = useDebounce(search, 500);

En lugar de enviar una solicitud con cada tecla pulsada, espere hasta que el usuario haga una pausa. Estos patrones son útiles para campos de búsqueda, filtros, listas largas y paneles de control con muchas partes en movimiento.

Reducir las solicitudes de red redundantes

La velocidad de renderizado es solo una parte del problema: demasiadas solicitudes pendientes pueden hacer que la aplicación parezca lenta incluso cuando su propio renderizado es rápido. Dependiendo de la situación, considere:

  • Almacenar en caché las respuestas
  • Deduplicar las solicitudes
  • Paginar los resultados
  • Atenuar la entrada de búsqueda
  • Cancelar las solicitudes que ya no son relevantes

Si un usuario escribe rápidamente

react
react performance
react performance optimization

probablemente no quiera que tres solicitudes separadas compitan al mismo tiempo. El principio subyacente sigue siendo simple: evite hacer que la red realice tareas que en realidad no se necesitan.

Usar la memorización de forma selectiva

React incluye tres primitivas comunes de memorización:

  • useMemo almacena en caché el resultado de un cálculo.
  • useCallback almacena en caché una referencia a una función entre renders.
  • React.memo puede omitir la vuelta a renderizar de un componente cuando sus props no han cambiado.

Un ejemplo típico:

const filteredUsers = useMemo(() => {
  return users.filter(user =>
    user.name.includes(search)
  );
}, [users, search]);

Pero la memorización no es gratuita: conlleva su propio costo adicional y añade complejidad al código circundante. Úsala cuando un cálculo sea realmente costoso, cuando un componente siga volviendo a renderizarse sin motivo, o cuando una referencia estable sea realmente importante más adelante en el árbol. No inviertas esfuerzos optimizando código que no esté causando un problema medible.

Deja que el compilador se encargue de parte del trabajo

Un añadido reciente a la cadena de herramientas de React, el React Compiler, puede aplicar automáticamente muchas de estas optimizaciones en su lugar: memorizar valores, funciones y componentes sin que usted tenga que hacerlo a mano en cada caso. Eso significa que ya no es necesario recurrir por defecto a:

useMemo(...)
useCallback(...)
React.memo(...)

No obstante, el React Compiler no elimina la importancia de verificar primero si realmente existe un problema de rendimiento. La secuencia correcta sigue siendo confirmar que hay un problema real, permitir que el compilador aplique las optimizaciones de las que es capaz y recurrir a la memorización manual solo cuando se tenga una razón concreta.

Proporcionar identidades estables a React

Las claves indican a React qué elemento de una lista es cual en cada renderizado. Es preferible obtener la clave a partir de una propiedad estable y única de los datos en lugar de de su posición:

items.map(item => (
  <Item key={item.id} />
));

Evite usar el índice del array como clave, ya que al reordenar, insertar o eliminar elementos, todos los índices por debajo del punto de cambio se modifican:

items.map((item, index) => (
  <Item key={index} />
));

Una clave estable permite a React detectar correctamente qué entradas se añadieron, eliminaron o actualizaron en lugar de hacer conjeturas basadas en la posición. El mismo principio se aplica a los objetos y funciones que pasa como props: crear un objeto o una función de callback completamente nueva en cada renderizado anula el propósito de un componente hijo memorizado, ya que sus props parecerán diferentes cada vez aunque no haya cambiado nada significativo.

Localice el verdadero cuello de botella antes de actuar

Una vez que te sientas cómodo con estas técnicas, no confíes en la intuición para decidir qué corregir. Abre el Profilador de React DevTools para ver exactamente qué componentes se renderizan y cuánto tiempo tarda cada uno. En el caso de problemas que van más allá de React mismo, el panel de Rendimiento del navegador puede revelar tareas prolongadas, ejecución lenta de scripts, trabajos costosos de maquetación o cuellos de botella en el renderizado. En lugar de asumir que “este componente se siente lento”, utiliza estas herramientas para determinar con precisión por qué es lento.

Confirma que la corrección realmente ayudó

Después de aplicar una optimización, vuelve a medir en lugar de asumir que funcionó. Verifica si el tiempo de renderizado realmente disminuyó, si el paquete se hizo más pequeño y si las interacciones parecen más rápidas. Si nada de esto mejoró, es posible que el cambio no fuera necesario desde un principio.

Una lista de verificación antes de publicar

Antes de publicar una aplicación React, revise si está renderizando interfaces de usuario que no necesita, si el estado se encuentra en el lugar adecuado, si está almacenando valores que podrían calcularse en su lugar, si las listas largas se manejan de manera eficiente, si el código pesado solo se carga cuando es necesario, si las búsquedas e interacciones siguen siendo rápidas, si está realizando llamadas a API innecesarias, si la memorización resuelve un problema real, si el React Compiler podría encargarse de alguna optimización, si sus claves son estables, si midió el verdadero cuello de botella y si verificó la mejora posteriormente.

En última instancia, la mejor optimización no es aquella que agrega más código, sino la que hace que React realice menos tareas innecesarias.

Lecturas relacionadas

  • Pre-renderización parcial y renderización concurrente explicada — Aprenda cómo la pre-renderización parcial de Next.js y la renderización concurrente de React solucionan el problema de las aplicaciones lentas al permitir que los frameworks programen y procesen tareas de forma secuencial en lugar de tratar las operaciones de renderizado como una sola unidad que bloquea todo.
  • Proposiciones TC39 en 2026: Decoradores, Temporal y Señales explicados — Un análisis práctico de tres propuestas del TC39: los decoradores nativos, la API Temporal y las Señales, y su impacto en los desarrolladores de JavaScript y TypeScript full-stack.
  • React 19.2 explicado: Activity, useEffectEvent y renderizado estático — Aprenda cómo el nuevo componente Activity, el hook useEffectEvent y el renderizado estático parcial de React 19.2 corrigen los costos ocultos de rendimiento en las interfaces de usuario modernas.