Utilice React Context para un entorno compartido, no como cajón de trastos.
El contexto distribuye valores a través de un árbol; no define la propiedad, actualiza los límites ni un modelo de estado. Prefiera proveedores especializados y herramientas reales para el estado del servidor.
Fuentes: Most Developers Use React Context for the Wrong Problems
El paso de propiedades no siempre es un fallo arquitectónico
React Context suele presentarse como una solución rápida. Un valor atraviesa tres componentes que nunca lo utilizan. Otra pantalla necesita al usuario actual. Un modal debe abrirse desde lo más profundo del árbol. Pasar propiedades de forma directa se vuelve tedioso, así que aparece un proveedor y el árbol de repente parece más ordenado.
Los componentes intermedios descartan las propiedades no utilizadas. Los hijos más profundos obtienen lo que necesitan a través de un hook. La estructura parece más intencionada que pasar el mismo argumento por cada capa.
Esa mejora estética puede ocultar una pregunta más importante: ¿debería ese valor estar en Contexto en absoluto?
Contexto resuelve un problema: un padre puede publicar un valor a sus descendientes sin tener que pasarlo por cada archivo intermedio. No decide dónde debe residir el estado, con qué frecuencia deben actualizarse los consumidores, si valores no relacionados deben compartir un contenedor o qué componente es el responsable de un flujo de trabajo.
Esa clase de decisiones permanecen intactas. El contexto puede ocultarlas porque el acceso se vuelve invisible; un hijo puede leer datos sin necesidad de declararlos en las propiedades. Un proveedor puede acumular campos no relacionados, ya que todo parece “global”. Una pequeña actualización puede llegar a docenas de consumidores que comparten un mismo objeto de contexto. Un widget supuestamente reutilizable puede depender de un proveedor que solo existe en una página.
La debilidad no radica en la API, sino en usar un acceso más sencillo como sustituto de una estructura de propiedad más clara.
La transmisión de propiedades tiene mala reputación porque la repetición es visible. Un valor comienza alto, atraviesa los componentes del diseño y finalmente llega al elemento que lo necesita. Las capas intermedias se asemejan a sistemas de plomería, por lo que Context parece una mejora.
A veces la repetición contiene información útil. Muestra el origen, qué ramas dependen del valor y hasta dónde llega esa dependencia. Las propiedades permiten buscar valores de entrada. Cuando el valor cambia o desaparece, su impacto se mantiene local y evidente.
Context borra ese camino. Un descendiente puede comenzar a procesarlo sin tener que actualizar los contratos intermedios. El árbol sigue dependiendo del proveedor, pero la relación ya no se muestra en la interfaz del componente.
Eso no significa que toda cadena larga de propiedades deba mantenerse. Cuando un subárbol grande comparte realmente una misma preocupación, el uso de hilos se vuelve un obstáculo. El error consiste en tratar cada propiedad repetida como prueba de que falta un contexto.
A veces el valor está demasiado elevado porque la propiedad no pertenece al lugar adecuado. Una página contiene un borrador de diálogo y lo envía a través de varias capas hasta un formulario. El contexto facilitaría el acceso, pero un diseño más sólido podría permitir que el formulario o el diálogo sean los responsables del borrador.
A veces la composición elimina ese paso intermedio. Un padre puede crear el elemento que necesita los datos y pasar ese elemento como children a un layout. El layout nunca llega a conocer esa dependencia.
Las propiedades repetidas son un síntoma. El contexto es solo una solución, y no constituye el diagnóstico por defecto.
El contexto no convierte de forma segura el estado local en estado global
Un patrón frecuente es inventar un contexto cada vez que varios componentes deben actualizar el mismo estado. El proveedor almacena valores, exporta funciones de configuración y cada descendiente puede leer o escribir en ellos.
A menudo comienza de forma sencilla:
const AppContext = createContext(null);
function AppProvider({ children }) {
const [sidebarOpen, setSidebarOpen] = useState(false);
const [activeModal, setActiveModal] = useState(null);
const [selectedRecord, setSelectedRecord] = useState(null);
return (
<AppContext.Provider
value={{
sidebarOpen,
setSidebarOpen,
activeModal,
setActiveModal,
selectedRecord,
setSelectedRecord,
}}
>
{children}
</AppContext.Provider>
);
}
El acceso se centraliza en un único lugar, pero el significado del estado sigue siendo confuso. La visibilidad de una barra lateral, qué modal está abierto y qué registro está seleccionado suelen corresponder a funcionalidades separadas y se actualizan en horarios distintos.
Cualquier hijo dentro del árbol puede entonces reescribir directamente el almacenamiento del proveedor. Una fila de tabla puede establecer la selección, abrir un modal y cerrar la barra lateral. Los componentes no expresan intenciones; simplemente acceden a campos compartidos y componen la combinación que necesitan.
Sigue un acoplamiento invisible. El modal asume que ya existe una selección. La tabla supone que cerrar el modal elimina la selección. La barra lateral reacciona a un flujo de trabajo que no le pertenece. Esas relaciones no se capturan en un reducer, una operación ni en los límites de una funcionalidad; existen únicamente porque muchos componentes pueden llamar a numerosos setters.
Al trasladar campos al Contexto solo se reubica el almacenamiento; la claridad en el significado no surge automáticamente.
Un proveedor más robusto expone operaciones en lugar de setters brutos, y solo para un objetivo coherente. Un sistema de diálogos podría ofrecer openDialog y closeDialog. Un proveedor de autenticación podría exponer datos de sesión y operaciones de inicio de sesión. Un proveedor de temas podría exponer el tema actual y un actualizador específico.
No se detenga en el hecho de que “varios componentes necesitan acceso”. Pregúntese si comparten un mismo ciclo de vida y una misma finalidad.
Un objeto de contexto puede generar una gran superficie de actualizaciones
El contexto también distribuye las actualizaciones. Cuando el valor del proveedor cambia, los consumidores de ese contexto pueden volver a renderizar. Eso está bien cuando el valor es un tema relevante y los consumidores dependen realmente de sus cambios.
Los problemas surgen cuando un proveedor combina valores con frecuencias de actualización no relacionadas.
function ApplicationProvider({ children }) {
const value = {
user,
theme,
locale,
notifications,
sidebarOpen,
featureFlags,
liveJobStatus,
};
return (
<ApplicationContext.Provider value={value}>
{children}
</ApplicationContext.Provider>
);
}
Un interruptor de tema no debería invalidar una tabla que solo necesitaba el registro seleccionado. Una marca de activación de barra lateral no debería actualizar un formulario que solo necesitaba el ID del usuario. Combinar esos campos en un único objeto hace que cada consumidor sea sensible a cada escritura, a menos que se memoricen cuidadosamente los datos y se dividan los contextos.
A veces, los equipos solo se dan cuenta del costo después de realizar un análisis. Para entonces, muchas funcionalidades ya han incorporado ese mecanismo complejo. Dividir los contextos requiere identificar a cada consumidor y decidir qué parte necesita realmente cada uno.
Preferir proveedores estrechos alineados con la frecuencia de actualizaciones. Colocar el código de interfaz de usuario con alto índice de cambios en un lugar y los datos de sesión estables en otro. Memorizar los valores del proveedor cuando la identidad del objeto cambiaría de lo contrario en cada renderizado. Medir antes de incorporar “solo un campo más” a una estructura existente.
El contexto puede ocultar los límites de componentes faltantes
Cuando conectar una funcionalidad resulta complicado, el contexto puede ocultar la ausencia de un límite claro. Un editor de productos que lee en silencio el estado del producto activo, las versiones en borrador, las validaciones, los permisos y las mutaciones desde el contexto a nivel de página solo tiene sentido dentro de esa misma página.
Algunas dependencias son apropiadamente implícitas porque describen el entorno en lugar de datos de funcionalidades: temas, localización, enrutamiento, un cliente HTTP compartido. Estas suelen ser generales y relativamente estables.
Los datos de las características son diferentes. Ocultarlos en Context elimina el ruido generado por los propios, pero aumenta la dependencia arquitectónica. Que falten algunos propios no significa que falten las dependencias; a menudo significa que esas dependencias quedan fuera de la vista.
Pregúntese si el componente podría entenderse y probarse fuera del proveedor que actualmente lo rodea. Si la respuesta es no, es posible que Context esté compensando por un límite que debería haberse definido mediante un contrato más claro de propios o composición.
Context se utiliza con frecuencia en lugar de una gestión adecuada del estado del servidor
Otro uso incorrecto es introducir los datos obtenidos en Context porque varios componentes los necesitan. El proveedor realiza la solicitud una sola vez, almacena la respuesta y expone los datos, así como información sobre el estado de carga, errores y posibles actualizaciones. Parece una capa de datos compartida sin necesidad de otra biblioteca.
El estado real del servidor requiere rápidamente más funcionalidades: deduplicación de solicitudes, reglas para datos obsoletos durante la validación, intentos repetidos, cancelación, paginación, actualizaciones posteriores a las mutaciones y la posibilidad de mantener los datos anteriores visibles al actualizar.
El contexto no proporciona nada de esto; solo reenvía lo que haya calculado el proveedor.
Poco a poco, el proveedor se convierte en una caché implementada manualmente. Los componentes dependen de sus peculiaridades al actualizarla. Las mutaciones corrigen manualmente los arrays compartidos. Diferentes proveedores crean formas ligeramente distintas de cargar datos y manejar errores. Dejar una ruta activa puede hacer que se pierda la caché; usar dos proveedores puede causar que se realicen dos búsquedas.
Existen bibliotecas de datos especializadas porque el estado remoto tiene un ciclo de vida diferente al del estado de la interfaz de usuario local: propiedad en el servidor, actualidad de los datos, caché, invalidación, concurrencia y modos de fallo.
El contexto puede distribuir un cliente configurado o una dependencia de funcionalidad estable. Rara vez es el lugar adecuado para reconstruir la coordinación de solicitudes con useState y useEffect habituales. Un proveedor enfocado que coordina los datos del servidor con un único flujo de trabajo sigue siendo válido, pero hacer que los datos sean accesibles globalmente no resuelve el problema de la sincronización.
Hacer que un payload sea accesible no es lo mismo que gestionar datos remotos.
El contexto no sustituye a un modelo de estado
Cuando un flujo de trabajo se vuelve complejo, los equipos a veces lo incluyen en el contexto para que cada paso pueda ver los mismos campos. Los procesos de pago, la onboarding, los editores y los formularios multietapa suelen terminar de esta manera.
Eso puede funcionar cuando el proveedor representa un único flujo de trabajo y expone transiciones controladas. Se vuelve frágil cuando el contexto es solo un conjunto de campos y funciones de modificación.
Un proveedor de pago puede contener campos de dirección, campos de pago, opciones de entrega, paso actual, errores de validación, indicadores de carga, indicadores de modalidad, totales y muchos otros parámetros configurables. Cada componente puede modificar cualquier parte de forma independiente.
Compartir el almacenamiento no es lo mismo que definir reglas. Cambiar de país podría requerir eliminar la opción de entrega; cambiar la forma de entrega podría necesitar volver a calcular los totales. Regresar al paso anterior puede mantener algunos campos y invalidar otros. Un fallo en el pago podría alterar el flujo sin borrar la dirección.
Estas relaciones necesitan un modelo de estado: reducidos u operaciones explícitas que codifiquen las transiciones válidas. El contexto puede distribuir ese modelo, pero no es el modelo en sí.
Sin reglas, aparecen combinaciones imposibles: pasos de pago sin entrega válida, valores de éxito y error ambos verdaderos, opciones de entrega que pertenecen a una dirección anterior. No es necesario contar necesariamente con una biblioteca formal de máquinas de estado. De todas formas, hay que especificar cuáles son los hechos independientes, qué combinaciones son válidas y qué eventos modifican varios campos en una sola transición.
El contexto permite acceder a un modelo adecuado. No puede salvar un modelo confuso simplemente elevándolo en el árbol de estructura.
El acceso global puede debilitar la propiedad
Los valores en el contexto son fáciles de leer y de escribir desde muchos lugares. La comodidad puede erosionar la sensación de propiedad.
Un contexto de notificaciones actualizado por gestores de red, formularios, páginas y tareas en segundo plano. Un contexto de selección modificado por tablas, diálogos, atajos y cambios de ruta. Un contexto de preferencias escrito directamente por cada control que muestra una configuración.
Cuando muchos escritores comparten una misma tienda, la responsabilidad por la corrección se vuelve difusa.
Un sentido de pertenencia más fuerte surge de operaciones más específicas. Prefiera addNotification, dismissNotification y clearExpiredNotifications en lugar de un método bruto como setNotifications. Prefiera signIn, signOut y refreshSession en lugar de un método genérico como setSession. Las operaciones tienen un significado claro y permiten que el proveedor mantenga las invariantes.
Incluso así, mantenga al proveedor enfocado. Un despachador universal para cada funcionalidad recrea un acoplamiento global bajo otro nombre.
Muchos componentes pueden leer información del usuario actual, pero muy pocos deberían reemplazar la sesión. El contexto se mantiene fácil de gestionar cuando el proveedor controla su ciclo de vida y los consumidores interactúan a través de un contrato sencillo.
Un proveedor de contexto puede convertirse en un monolito oculto
Los componentes de gran tamaño se hacen notar por la longitud de sus archivos. Los proveedores de gran tamaño permanecen más silenciosos durante más tiempo.
El diseño puede parecer modular, con componentes y ganchos pequeños, mientras que un solo proveedor se encarga del estado de la aplicación, las llamadas a red, la persistencia, los eventos del navegador, los permisos, las notificaciones, los modales y los valores derivados. Los archivos de los componentes se mantienen cortos porque el proveedor se encarga de la coordinación.
Aún así se trata de un monolito; simplemente renderiza poco JSX.
El proveedor cambia por razones no relacionadas. Las reglas de notificaciones, las necesidades del tema, los cambios de sesión y los flujos de trabajo de modales modifican al mismo módulo. Cada consumidor depende de su estabilidad. Las pruebas se amplían porque las operaciones interactúan a través del estado compartido. El rendimiento disminuye cuando se propagan las actualizaciones. Los refactores obligan a desenredar suposiciones integradas en un valor utilizado en todas partes.
La solución no consiste en un anidamiento interminable por sí mismo. El anidamiento es aceptable cuando cada proveedor tiene una responsabilidad clara. Unos pocos proveedores bien definidos suelen ser más claros que un único objeto que finge compartir un mismo ciclo de vida.
También es importante el lugar donde se ubica algo. El contexto necesario para una ruta específica debe abarcar solo ese subárbol, y no toda la aplicación. Una ubicación más restringida reduce tanto las dependencias como el alcance de las actualizaciones.
La disponibilidad global debe ganarse, y no ser el lugar por defecto para almacenar propiedades incómodas.
El contexto es excelente para los problemas que realmente resuelve
El contexto brilla cuando un subárbol comparte realmente una dependencia ambiental o una preocupación estable.
Los temas son adecuados porque muchos descendientes necesitan la misma opción de presentación. La localización es apropiada ya que el formato y la traducción se aplican de manera generalizada. Un cliente API compartido, el entorno de enrutamiento, un ámbito de formulario o un flujo de trabajo único también pueden formar parte de ello cuando todos los consumidores se encuentran dentro de un mismo límite.
El valor no tiene por qué ser estático. Debe tener un significado coherente y un patrón de actualización que se ajuste a los consumidores.
Un Contexto bien diseñado responde: ¿en qué entorno se ejecuta este subárbol?, ¿qué dependencia comparten estos componentes? y ¿de qué flujo de trabajo específico forman parte?
Un Contexto mal diseñado responde: ¿qué valores resultaron molestos de pasar como propiedades? Esa pregunta genera estados no relacionados, superficies de actualización amplias, dependencias ocultas y setters en bruto. La estructura interna desaparece mientras se vuelve más difícil discernir quién es el responsable de cada elemento.
Antes de agregar un Contexto, pregúntese si existe un subárbol significativo que comparta el mismo valor, si los consumidores comparten un ciclo de vida, con qué frecuencia cambia el valor y si el proveedor puede publicar un contrato estrecho y estable. También pregúntese qué problema ocultaría el Contexto. El estado mal colocado, las fronteras de las funcionalidades faltantes, la composición débil, el caché del estado del servidor o un flujo de trabajo indefinido no se solucionarán con un acceso más sencillo.
El Contexto debe distribuir las decisiones, no evitarlas
La mayoría de los usos incorrectos no provienen de una mala comprensión de la API, sino del alivio inmediato que brinda. La cadena de propiedades desaparece, los componentes obtienen acceso y una funcionalidad se lanza sin necesidad de reorganizar la estructura. Hoy en día es más fácil escribir código.
El costo surge cuando el acceso supera al control de la propiedad. Ciclos de vida no relacionados comparten un mismo proveedor. Los consumidores vuelven a procesar información por cambios que ignoran. Los componentes funcionales dependen de entornos ocultos. Los proveedores se convierten silenciosamente en almacenes improvisados, cachés de solicitudes y motores de flujo de trabajo que nunca recibieron un modelo explícito.
Llamar a Context una biblioteca de estado deficiente ignora el propósito real. Nunca pretendió definir la gestión del estado; solo transmite un valor a lo largo de una estructura jerárquica.
El diseño general sigue teniendo que responder sobre qué representa ese valor, qué módulo lo controla, quién tiene permiso para modificarlo, con qué frecuencia se generan actualizaciones y qué consumidores realmente pertenecen a él.
Context es más eficaz una vez que esas decisiones ya están claras.
Lecturas relacionadas
- Contexto vs Redux Toolkit vs Zustand para el state de React — Compare Context, Redux Toolkit y Zustand para un portal de videos, y luego combine las tiendas del cliente con TanStack Query para el state del servidor.
- Frentends de JavaScript autoadaptativos para interfaces gestionadas por IA — Cómo la transmisión en tiempo real, la recuperación de vectores, el state reactivo, los componentes dinámicos y la observabilidad transforman la arquitectura del cliente para interfaces nativas de IA.
- Zustand en práctica: Tiendas pequeñas, seleccionadores precisos, rastreador de plantas — Reemplace el uso de prop-drilling y los procedimientos complejos de Redux con create(), seleccionadores, acciones asíncronas, middleware persist/devtools, slices y una aplicación de rastreo de plantas.