Patrón de Componentes Compuestos (Compound Components)

🧩 ¿Qué es el patrón de Componentes Compuestos?

Este patrón te permite construir componentes que trabajan juntos compartiendo un estado implícito mediante React Context. Es ideal para construir piezas reutilizables de UI que deben funcionar en conjunto, como:

  • Pestañas (Tabs)
  • Acordeones
  • Desplegables (Dropdowns)
  • Modales
  • Formularios en pasos (Step Forms)
  • Interruptores (Toggles)
  • Otros grupos interactivos de UI

✅ ¿Por qué usarlo?

Agrupa componentes relacionados semánticamente, lo que significa que están conectados lógicamente en propósito y estructura.

Cuando decimos «semántico», nos referimos a que los componentes describen claramente su rol y cómo deben usarse juntos (como <Tabs> y <Tab>, que están lógicamente conectados).


🚫 Cuándo evitarlo

1. Estructuras profundamente anidadas o dinámicas

Si tu estructura de componentes es muy anidada o muy dinámica (por ejemplo, si los hijos se generan desde una API externa o mediante herramientas de terceros), podrías perder el control sobre cómo se conectan al contexto.

A medida que tu UI se vuelve más compleja, manejar cómo cada componente se conecta al contexto compartido puede volverse difícil. Esto no es una falla del patrón en sí, sino una limitación de React Context.

¿Por qué?

React Context depende de que los componentes estén dentro del árbol del Provider. Si un hijo es:

  • Renderizado condicionalmente
  • Movido fuera del Provider

…es posible que ya no reciba el contexto esperado, lo que puede resultar en valores undefined o comportamiento incorrecto.

2. Necesitas que los componentes funcionen de forma independiente

Por ejemplo, usar un <Tab /> fuera de un contexto <Tabs /> fallará a menos que implementes la lógica manualmente.

3. La UI es extremadamente simple

A veces solo necesitas usar props y estado — un setup con componentes compuestos podría agregar complejidad innecesaria.

4. Ya usas un gestor de estado global

Si ya estás usando Redux o Zustand, estos te ofrecen una funcionalidad similar a context pero a nivel global, sin depender de la estructura del árbol de componentes. Puedes combinarlos con componentes compuestos para mayor flexibilidad y rendimiento.


💡 Ejemplo: Pestañas manuales vs componentes compuestos

❌ Implementación manual

<TabList tabs={['Inicio', 'Perfil']} onTabChange={setActiveTab} />
<TabContent activeTab={activeTab} content={['Contenido de Inicio', 'Contenido de Perfil']} />

Estás sincronizando manualmente el estado activeTab y coordinando los encabezados con el contenido.

A medida que crece la funcionalidad (eliminación dinámica, soporte de teclado), mantener esta lógica se complica.

✅ Versión con componentes compuestos

<Tabs>
  <Tabs.Tab id="inicio">Inicio</Tabs.Tab>
  <Tabs.Tab id="perfil">Perfil</Tabs.Tab>

  <Tabs.Panel id="inicio">Contenido de Inicio</Tabs.Panel>
  <Tabs.Panel id="perfil">Contenido de Perfil</Tabs.Panel>
</Tabs>

En esta estructura, el estado y la interacción están centralizados, eliminando la necesidad de coordinación manual.



🔧 Explicación de funcionalidades avanzadas

🔄 Soporte de teclado

const handleKeyDown = (e) => {
  if (e.key === 'ArrowRight') {
    setActiveTab(prev => (prev + 1) % tabs.length);
  }
  if (e.key === 'ArrowLeft') {
    setActiveTab(prev => (prev - 1 + tabs.length) % tabs.length);
  }
};

return (
  <div onKeyDown={handleKeyDown} tabIndex={0}>
    {/* Renderizar pestañas */}
  </div>
);

Con componentes compuestos, esta lógica puede encapsularse y estar disponible automáticamente.

❌ Eliminación manual de pestañas

const [tabs, setTabs] = useState(['Usuarios', 'Configuración', 'Logs']);

const removeTab = (tabToRemove) => {
  setTabs(prev => prev.filter(tab => tab !== tabToRemove));
  if (activeTab === tabToRemove) {
    setActiveTab(tabs[0]); // Fallback
  }
};

✅ Eliminación con componentes compuestos

Un componente compuesto puede manejar la sincronización del tab activo y actualizar automáticamente el panel correspondiente.


🧠 Patrones de diseño relacionados

El patrón de componentes compuestos no es una implementación directa de patrones clásicos de POO, pero comparte similitudes:

🧭 Mediador (Mediator)

El componente padre (por ejemplo <Tabs>) centraliza la comunicación. Los hijos no se comunican entre ellos directamente — solo con el padre.

Publicaciones Similares

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *