Patrón Compound Components en React: Creación de un Componente de Tabs
Resumen
Aprende a implementar compound components en React para construir un sistema de tabs modular, flexible y tipado con TypeScript. El componente padre controla el estado, los hijos renderizan el contenido. Un enfoque claro para escalar interfaces sin perder simplicidad.
¿Qué es el patrón compound components en React y cómo se aplica a tabs?
El patrón compound components coordina varios componentes que trabajan juntos. El padre decide qué mostrar y el hijo se enfoca en renderizar. Aquí, el padre es un wrapper llamado Tabs y los hijos son Tab: el primero gestiona el estado local y los segundos muestran label y children.
El padre controla el activeIndex y cambia al hacer clic.
Los hijos solo reciben props y renderizan contenido.
La composición hace el sistema más modular y flexible.
¿Qué props y tipos se definen?
Tab recibe: label: string y children: ReactNode.
Tabs recibe: children: ReactNode.
Se tipa con TypeScript usando interfaces y React.FC.
¿Qué utilidades de React se emplean?
Estado con useState para el índice activo.
Normalización de hijos con React.Children.toArray.
Validación con React.isValidElement.
Manejo de eventos con onClick.
Uso de key para cada elemento de lista.
Código del componente hijo Tab:
importReact,{ReactNode}from'react';interfaceTabProps{ label:string; children:ReactNode;}exportconstTab:React.FC<TabProps>=({ label, children })=>{return(<div><em>{label}</em><span>{children}</span></div>);};
¿Cómo se construye el componente tabs con estado y validación de hijos?
El componente Tabs necesita un estado local para saber qué pestaña está activa y una función para actualizarla al hacer clic. Además, convierte los hijos a arreglo y filtra solo elementos válidos de React. Luego, renderiza una lista de labels y un área de contenido.
Se inicializa activeIndex en 0.
Se define handleTabClick(index: number) para cambiar el estado.
Se crea tabElements con Children.toArray(children) y isValidElement.
Se mapea para pintar un <li> por cada tab con su label.
Se muestra el contenido de tabElements[activeIndex] dentro de tabContent.
¿Cómo se usa en el componente padre con etiquetas y contenido?
Una vez definidos Tabs y Tab, se reemplaza el contenedor por Tabs y se agregan varios Tab con su label y children. Al hacer clic, cambia el contenido activo sin lógica extra en los hijos.
El padre define las pestañas.
Cada hijo incluye su label y su contenido.
La interacción actualiza el índice activo y renderiza el nodo correspondiente.
¿Te gustaría aportar mejoras o mostrar tu implementación de tabs con compound components? Comparte tus ideas y tu landing de ejemplos en los comentarios.
Cuando utilizar type vs interface a la hora de definir los props?
Depende mucho de como quieras utilizar el componente, por lo regular se utilizan mas las interfaces, por que se puede extender como este ejemplo de abajo
ahi esta extendiendo la interfas de props con todas las propiedades de <u>React.PropsWithChildren</u>
ya no tendrias que estar escribiendo de una por una, seria todas las propiedades que tiene React.PropsWithChildren mas las tuyas personalizadas como en este caso label, con la interface se puede hacer eso, con los types no tan facil
Si usas "interface" obligas a tu componente de react a que tiene que recibir todas las props que definas en la interface, mientras que, si usas "type" tu componente no necesariamente debe recibir todas las props que definas en el type.
Veo mi reflejo en la clase, al estudiar inglés se suele perder algo de ortografía como jirafa... pero de resto muy interesante, hay prácticas que uso y no conozco el nombre, solo las copio de otros devs al leer otros repos, muy interesante el ejemplo, gracias
Hahahaha gracias por notarlo, Jeisson !!! 😅
Que buen curso carajo!
Este patrón me parece súper interesanteee y la forma en que accedemos desde Tabs a las props de Tab era algo que nunca habia visto, súper interesante y muy buena clase
Ahora entiendo de donde viene que el codigo de la AI este lleno de emojis :)
¿No entendiste el filtrado del array "tabElements" y el uso de "child is ReactElement"?
Lee las siguientes láminas y entenderás. Lo mismo me pasó a mi.
Si te sale este error:
ReactElement is a type and must be imported using a type-only import when "verbatimModuleSyntax" is enabled
al importart "ReactElement" en el minuto 9:12 del video, es por lo siguiente:
Esto significa que ReactElementes solo un tipo, no un valor en tiempo de ejecución. Cuando verbatimModuleSyntax está activado, TypeScript te obliga a diferenciar entre importaciones de valores y importaciones de tipos.
📢Solución: Importar de la siguiente forma
import type { ReactElement } from "react";
Muchas Gracias
exportconstCompoundComponents=()=>{return(<Tabs><Tab label="Gato">Miauuu</Tab><Tab label="Perro">Wouuu</Tab><Tab label="Pollito">Pioo</Tab></Tabs>);};```a un estoy procesando como es que Tabs recibió los hijos jejeje Estasuper
El caso que muestras parece más un componente hibrido, ya que en control props un componente no tiene estado interno.
El bloque const tabElements = React.Children.toArray(children).filter((child): child is ReactElement => React.isValidElement(child)) cumple con dos funciones:
Convertir a Array: React.Children.toArray(children) transforma los hijos (children) en un array. Esto facilita su manipulación y permite que se pueda aplicar métodos de array como filter.
Filtrar Elementos Válidos: El método filter elimina aquellos elementos que no son válidos de React. La función de comprobación React.isValidElement(child) asegura que solo se mantengan los elementos que son instancias de componentes React, descartando cualquier valor que no cumpla con esta condición.
Este patrón es fundamental para asegurar que tu componente solo maneje elementos React válidos y evita errores en la renderización.