Eliminación del envoltorio del proveedor: contexto de renderizado desde componentes del servidor
Aprenda cómo React 19.3 permite que un componente de servidor renderice directamente un contexto de cliente, cómo Flight lo serializa, y qué limitaciones en la lectura y los valores siguen aplicándose.
Fuentes: React 19.3: Render a Context Directly in a Server Component, No Provider Wrapper
Casi todos los repositorios de código de React Server Components contienen un pequeño archivo 'use client' cuya única función es crear un Context y exportar un componente proveedor que transfiere una sola propiedad. Ese contenedor existía porque a los componentes de servidor no se les permitía renderizar el Context en sí. React 19.3 elimina esa restricción, por lo que ahora el servidor puede renderizar directamente el elemento Context y se puede eliminar el contenedor.
Esta guía explica el patrón antiguo, el nuevo, qué hace realmente el protocolo Flight en su interior, y los problemas que determinan si el cambio funciona en tu framework actual. Los ejemplos requieren un entorno de ejecución real para Server Components (un framework con soporte para el bundler RSC y un servidor), por lo que deben considerarse como código de referencia completo y no como algo para pegar en un entorno aislado. Para obtener información sobre cómo se integran las partes servidor y cliente de una aplicación RSC, consulta nuestra descripción general de la arquitectura detrás de React Server Components.
El patrón antiguo: un componente que solo reenvía una propiedad
Los componentes del servidor no pueden llamar a createContext, ya que un Context es una máquina de estado del lado del cliente. Sin embargo, el árbol del cliente que se encuentra debajo de ellos necesita con frecuencia datos que ya posee el servidor, como el usuario iniciado de sesión, las banderas de funcionalidad del inquilino o la configuración regional de la solicitud. La única vía para cruzar esta barrera eran los props de un componente del cliente, y ese componente del cliente a su vez renderizaba al proveedor.
El módulo del cliente comienza con la directiva e importación habituales.
'use client';
import { createContext } from 'react';
Luego crea el Context y exporta un proveedor cuyo cuerpo no hace nada más que pasar currentUser como value.
export const UserContext = createContext(null);// exists only to forward one prop
export function UserProvider({ currentUser, children }) {
return (
<UserContext value={currentUser}>
{children}
</UserContext>
);
}
El lado del componente del servidor
El layout, que es un componente del servidor, importa al wrapper en lugar del Context.
import { UserProvider } from './user-context';
Obtiene al usuario en el servidor y se lo pasa al wrapper como un prop.
export async function Layout({ children }) {
const currentUser = await getCurrentUser();
return (
<UserProvider currentUser={currentUser}>
{children}
</UserProvider>
);
}
Si intentas omitir el contenedor en React 19.2, te encuentras con un error de tiempo de ejecución confuso que comienza con “Element type is invalid” y luego indica que un tipo de elemento perezoso se resolvió como un Contexto en lugar de una clase o función. Parece ser un problema del bundler, pero la causa está en el renderizador. Un Contexto importado más allá de los límites del cliente llega al renderizador del servidor como una referencia de cliente que se resuelve de forma perezosa, y 19.2 solo podía convertir esas referencias en componentes. Un objeto Contexto nunca fue un resultado aceptable.
El nuevo patrón: renderizar el Contexto mismo
En 19.3, el módulo del cliente se reduce a la directiva, la importación y una única exportación.
'use client';
import { createContext } from 'react';
Ya no existe componente alguno, solo el objeto Contexto.
export const UserContext = createContext(null);
Renderizarlo desde el layout
El layout ahora importa directamente UserContext.
import { UserContext } from './user-context';
El JSX mantiene la misma estructura que antes, con la diferencia de que el elemento es Context y la propiedad se denomina value.
export async function Layout({ children }) {
const currentUser = await getCurrentUser();
return (
<UserContext value={currentUser}>
{children}
</UserContext>
);
}
Uso en el cliente
No hay cambios para los consumidores. Un componente de cliente ubicado en cualquier parte debajo del layout importa el mismo Context y lo lee mediante use.
'use client';
import { use } from 'react';
import { UserContext } from './user-context';
Aquí el menú de avatares obtiene al usuario del Context y muestra su imagen.
export function AvatarMenu() {
const user = use(UserContext);
return <img src={user.avatar} alt={user.name} />;
}
Preste atención a la sintaxis de React 19 en el layout: se muestra <UserContext value> en lugar de <UserContext.Provider value>. La forma con .Provider sigue funcionando en el cliente, pero el servidor muestra el objeto Context directamente, por lo que debe utilizarse la forma más corta en el código del servidor.
Qué hace Flight y qué no incluye esta función
Cuando un componente de servidor renderiza <UserContext value={currentUser}>, no se ejecuta ninguna lógica de Contexto en el servidor. El serializador de Flight emite un elemento del tipo referencia al cliente, que apunta al módulo user-context.js y a su exportación UserContext. Ese elemento lleva { value: currentUser, children } como sus propiedades. Dichas propiedades se serializan exactamente igual que cualquier otra propiedad que se envía al cliente. Una vez que el navegador carga ese módulo, la referencia al cliente se convierte en el objeto Contexto real, y React establece un proveedor que contiene el valor transmitido. La solución para el renderizador, registrada en facebook/react #35675, enseñó al camino de código de tipo perezoso a aceptar un objeto Contexto además de funciones, memo y forwardRef.
De ese diseño se derivan dos consecuencias.
El valor debe ser serializable
El valor de Contexto sigue las mismas reglas que cualquier propiedad que llega al cliente. Los tipos permitidos incluyen primitivos como cadenas y números, objetos simples y arrays, instancias de Date, Map y Set, promesas, elementos JSX y funciones del servidor. Las funciones comunes, instancias de clases y cualquier elemento con un prototipo personalizado no están permitidos. Un registro simple currentUser funciona; una instancia de UserService con métodos no lo hace. La referencia 'use client' detalla la lista completa.
No se trata de Contexto del lado del servidor
La solicitud de integración indica claramente que aún no se puede llamar a use(Context) dentro de un Componente del Servidor. Los Componentes del Servidor no tienen forma de leer un Contexto, ya que no existe proveedor en el servidor al que puedan acceder. Esta funcionalidad es simplemente un camino más corto para enviar un valor al árbol del cliente.
Por qué vale la pena eliminar el revestimiento
Cada valor compartido solía costar un archivo y un componente. Eso parece algo menor hasta que se los cuenta: una aplicación con contextos para el usuario, tema, configuración regional, flags de funcionalidades, tenant y análisis lleva seis envoltorios casi idénticos, y cada uno representa una oportunidad de olvidar un parámetro o escribir mal un valor por defecto. Con el nuevo formato, el proveedor se encuentra justo donde se obtienen los datos, y el modelo mental de “qué partes funcionan en el servidor y cuáles en el cliente” pierde un caso especial.
La reacción de la comunidad ante este cambio fue mayormente de alivio en lugar de entusiasmo. Los desarrolladores comentaron que resultaba extraño que esto no estuviera soportado desde un principio, ya que un valor de Contexto es en realidad solo datos almacenados en memoria. Esa intuición es correcta, y explica por qué la corrección en React en sí fue sencilla.
Puntos a tener en cuenta antes de migrar
- Su framework debe incluir los paquetes de Flight 19.3. El cambio se encuentra en
react-server-dom-webpack,-turbopacky-parcelademás del reconciler, no solo enreact. Verifique la versión de React que establezca su framework; una versión incompatible genera el mismo error “Element type is invalid” mencionado anteriormente. - Defina el Contexto en un archivo marcado con
'use client'. Un Contexto definido en código de servidor no puede renderizarse de esta manera, ya que todo el mecanismo depende de que el objeto exista en el paquete del cliente. - Sigue siendo imposible leer datos en el servidor. Un componente de servidor que necesite ese valor debe recibirlo como argumento o obtenerlo por sí mismo;
use(Context)lanza un error en ese caso.
<UserContext value={getCurrentUser()}> sin await transmite el valor, y los consumidores deben desenvolverlo dos veces con use(use(UserContext)). Esa puede ser una técnica útil de transmisión en flujo, pero también es un error fácil de cometer al omitir un await.createContext(null) se aplica donde no haya un proveedor por encima de un consumidor, y mostrar el proveedor en el servidor no altera eso.Mantenerse por ahora con el envoltorio
Si su framework aún no ha adoptado la versión 19.3, mantenga el wrapper. El patrón UserProvider funciona en todas las versiones de React 19 y en cualquier framework. Cuando realice la actualización, la migración es sencilla:
- Elimine la función wrapper del módulo del cliente.
- Importe el Contexto en el componente del servidor en lugar del wrapper.
- Cambie el nombre de la propiedad reenviada a
value.
Puede migrar archivo por archivo a medida que su framework avanza hacia la versión 19.3. No es necesario convertir todo de una sola vez, y una vez que ambas formas funcionen no hay diferencia en el rendimiento entre ellas.
Puntos clave
- En React 19.3, un componente del servidor puede renderizar un Contexto exportado desde un módulo
'use client', por lo que los wrappers de proveedor de uso único se vuelven innecesarios.