Qué significa “pensar en componentes”
En React, una interfaz no se diseña como una página “entera”, sino como un conjunto de piezas pequeñas que se combinan. Un componente es una unidad de UI con una responsabilidad concreta: recibe datos (props), decide qué mostrar y puede delegar partes a otros componentes. La idea central es la composición: construir pantallas ensamblando componentes, igual que un árbol donde cada nodo puede tener hijos.
Composición: el “superpoder” de React
Componer significa que un componente puede contener a otros y pasarles información o “huecos” donde renderizar contenido. Esto evita duplicación y permite reutilizar estructuras.
// Un componente contenedor de layout que compone contenido dentro (children) function Card({ title, children }) { return ( <section className="card"> <h3>{title}</h3> <div className="card__body">{children}</div> </section> ); } // Uso: compones Card con contenido diferente <Card title="Resumen"> <p>Total: $120</p> </Card>Cómo descomponer una pantalla en componentes (guía paso a paso)
Usa este proceso cada vez que tengas una pantalla nueva (o una UI que creció demasiado).
Paso 1: Identifica la pantalla y su objetivo
Describe en una frase qué hace la pantalla. Ejemplo: “Listado de productos con filtros y carrito lateral”. Esa frase te ayuda a detectar dominios (productos, filtros, carrito).
Paso 2: Marca secciones visuales (layout)
Divide por regiones: encabezado, barra lateral, contenido principal, pie, paneles. Estas regiones suelen convertirse en componentes de layout.
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
- Header
- Sidebar (filtros)
- Main (lista)
- CartPanel
Paso 3: Dentro de cada sección, detecta patrones repetidos
Todo lo que se repite (tarjetas, filas, ítems, chips, botones con el mismo estilo) es candidato a componente reutilizable.
- Producto repetido →
ProductCard - Filtro repetido →
FilterOptionoCheckboxField - Ítem del carrito repetido →
CartItemRow
Paso 4: Separa “qué se ve” vs “de dónde vienen los datos”
Una regla práctica: si un componente solo renderiza UI en base a props, es de presentación. Si un componente orquesta datos, decide qué props pasar, maneja estados de pantalla o coordina acciones, es un contenedor (o “smart component”).
| Tipo | Responsabilidad | Señales típicas |
|---|---|---|
| Presentación | Renderizar UI | Recibe props, no conoce APIs, no decide flujos |
| Contenedor | Conectar datos y acciones | Coordina estados, transforma datos, decide qué mostrar |
Ejemplo de separación:
// Presentación: no sabe de dónde vienen los productos function ProductListView({ products, onAddToCart }) { return ( <ul className="grid"> {products.map(p => ( <li key={p.id}> <ProductCard product={p} onAdd={() => onAddToCart(p.id)} /> </li> ))} </ul> ); } // Contenedor: decide qué productos pasar y qué hacer al agregar function ProductListContainer() { // aquí viviría la lógica de datos/estado de pantalla const products = []; const handleAddToCart = (id) => {}; return <ProductListView products={products} onAddToCart={handleAddToCart} />; }Paso 5: Dibuja el árbol de componentes
Antes de escribir código, escribe el árbol. Ejemplo:
ShopPage (contenedor) ├─ Header (presentación) ├─ FiltersSidebar (contenedor o presentación + callbacks) │ ├─ PriceFilter (presentación) │ └─ CategoryFilter (presentación) ├─ ProductListContainer (contenedor) │ └─ ProductListView (presentación) │ └─ ProductCard (presentación) └─ CartPanelContainer (contenedor) └─ CartPanelView (presentación) └─ CartItemRow (presentación)Este árbol te obliga a decidir límites: qué vive arriba, qué se delega abajo, y qué se repite.
Criterios prácticos para decidir cuándo crear un componente nuevo
1) Regla de repetición (DRY con intención)
Si copiaste el mismo bloque de JSX 2–3 veces con pequeñas variaciones, crea un componente. Pero evita “componentizar” demasiado pronto si aún no conoces las variaciones reales.
2) Regla de responsabilidad única
Si el componente hace “demasiadas cosas” (renderiza lista, maneja filtros, valida formularios, abre modales), sepáralo. Un buen componente se puede describir con un nombre concreto: “muestra una tarjeta de producto”, “renderiza un selector de cantidad”.
3) Regla de complejidad visual
Si el JSX ocupa mucho espacio y cuesta “ver” la estructura, extrae subcomponentes. Señales: demasiados div, condicionales anidados, o secciones largas con comentarios tipo “// sección X”.
4) Regla de variación
Si una pieza tiene variaciones claras (tamaños, estados, estilos), conviene un componente con props explícitas en lugar de duplicar.
// Mejor: variación explícita <Button variant="primary" size="sm" /> <Button variant="secondary" size="lg" />5) Regla de frontera de reutilización
Antes de extraer, decide si el componente es:
- Específico de una pantalla (vive cerca de esa pantalla).
- De un dominio (reutilizable dentro del mismo dominio: “productos”, “carrito”).
- Genérico de UI (botones, inputs, modales; reutilizable en toda la app).
Esto evita crear componentes “medio genéricos” que terminan con props confusas.
Cómo nombrar componentes (convenciones útiles)
- PascalCase para componentes:
ProductCard,CartPanel. - Nombre por intención, no por implementación: mejor
PriceFilterqueSliderWithLabelsi su propósito es filtrar precio. - Evita nombres vagos:
Section,Wrapper,Container(salvo que sea realmente un contenedor genérico de layout). - Usa sufijos con criterio:
...View(presentación),...Container(orquestación),...Item/...Row(elementos de listas).
Props: nombres que cuentan una historia
Las props deberían leerse como una frase:
isOpen,isLoading,hasErrorpara booleanos.onAdd,onClose,onSelectCategorypara callbacks.selectedCategoryIden lugar devaluesi el contexto lo requiere.
Organización de carpetas: por dominio o por tipo
No hay una única forma “correcta”; elige la que reduzca fricción en tu proyecto.
Opción A: Por dominio (recomendable cuando la app crece)
Agrupa por áreas del negocio. Ventaja: todo lo relacionado vive junto (componentes, estilos, utilidades).
src/ features/ products/ ProductList/ ProductListContainer.jsx ProductListView.jsx ProductCard.jsx api.js selectors.js cart/ CartPanel/ CartPanelContainer.jsx CartPanelView.jsx CartItemRow.jsx pages/ ShopPage.jsx ui/ Button.jsx Modal.jsxOpción B: Por tipo (simple para proyectos pequeños)
Agrupa por categorías técnicas. Ventaja: fácil de entender al inicio; desventaja: puede dispersar el dominio.
src/ components/ ProductCard.jsx CartPanel.jsx containers/ ProductListContainer.jsx pages/ ShopPage.jsx ui/ Button.jsxRegla práctica para elegir
- Si tienes varias pantallas y cada una tiene piezas propias, usa dominio.
- Si es una app pequeña o un prototipo, por tipo puede ser suficiente.
Cómo evitar componentes demasiado grandes
Señales de alerta
- El archivo supera ~200–300 líneas y te cuesta encontrar cosas.
- Hay muchas ramas condicionales (
if, ternarios) mezcladas con JSX. - El componente renderiza “subsecciones” completas (header, tabla, formulario, modal) en el mismo archivo.
- Las props crecen sin control (más de ~8–10 props con nombres poco claros).
Técnicas de refactor
- Extrae subcomponentes por sección:
OrderSummary,ShippingForm,PaymentMethod. - Extrae componentes de lista: un
...Listy un...Item. - Extrae componentes de estado visual:
EmptyState,LoadingState,ErrorState. - Reduce props con objetos de dominio: pasar
productcompleto suele ser más claro que 10 props sueltas (siempre que el componente realmente opere sobre “un producto”).
Ejercicios guiados: descomposición en árbol de componentes
Ejercicio 1: Pantalla “Lista de tareas”
Descripción: pantalla con título, input para agregar tarea, filtros (Todas/Activas/Completadas) y lista de tareas con checkbox y botón eliminar.
Guía:
- Marca regiones: encabezado, formulario, filtros, lista.
- Detecta repetición: cada tarea es un ítem repetido.
- Decide contenedor vs presentación: ¿quién mantiene el estado de filtros y lista?
Árbol sugerido:
TodoPage (contenedor) ├─ PageHeader (presentación) ├─ AddTodoForm (contenedor o presentación + callbacks) ├─ TodoFilters (presentación) │ └─ FilterTab (presentación) └─ TodoList (presentación) └─ TodoItem (presentación)Ejercicio 2: Pantalla “Detalle de producto”
Descripción: galería de imágenes, título, precio, selector de cantidad, botón “Agregar”, pestañas (Descripción/Reseñas) y lista de reseñas.
Guía:
- Separa componentes de layout: columna izquierda (galería) y derecha (info).
- Identifica componentes con variación: pestañas, selector de cantidad.
- Define límites de reutilización:
Tabspodría ser UI genérica;ReviewItemes de dominio.
Árbol sugerido:
ProductDetailPage (contenedor) ├─ ProductGallery (presentación) │ ├─ ThumbnailList (presentación) │ └─ MainImage (presentación) ├─ ProductInfo (presentación) │ ├─ PriceTag (presentación) │ ├─ QuantitySelector (presentación) │ └─ AddToCartButton (presentación) └─ ProductTabs (contenedor o presentación) ├─ DescriptionPanel (presentación) └─ ReviewsPanel (contenedor) └─ ReviewItem (presentación)Ejercicio 3: Refactor de un componente “gigante”
Situación: tienes un CheckoutPage.jsx con formulario, resumen, validaciones visuales, estados de carga y un modal de confirmación, todo junto.
Pasos:
- Subraya en el JSX las secciones: Datos personales, Dirección, Pago, Resumen, Modal.
- Extrae primero componentes de presentación:
CheckoutSummary,CheckoutModal,PaymentForm. - Deja en el contenedor la orquestación: qué se muestra, qué callbacks se pasan, y cómo se compone el layout.
- Revisa props: si un subcomponente recibe demasiadas, agrupa por objetos (
customer,address) o crea subcomponentes más específicos.
Checklist rápido antes de dar por “bueno” un árbol de componentes
- ¿Cada componente tiene una responsabilidad que puedes explicar en una frase?
- ¿Los componentes de presentación podrían reutilizarse con otras fuentes de datos?
- ¿El árbol evita pasar props innecesarias por muchos niveles (sin forzar)?
- ¿Los nombres reflejan intención y dominio?
- ¿La organización de carpetas hace fácil encontrar dónde vive cada pieza?