Mentalidad de componentes en React: pensar la UI como piezas reutilizables

Capítulo 1

Tiempo estimado de lectura: 8 minutos

+ Ejercicio

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.

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

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 → FilterOption o CheckboxField
  • Í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”).

TipoResponsabilidadSeñales típicas
PresentaciónRenderizar UIRecibe props, no conoce APIs, no decide flujos
ContenedorConectar datos y accionesCoordina 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 PriceFilter que SliderWithLabel si 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, hasError para booleanos.
  • onAdd, onClose, onSelectCategory para callbacks.
  • selectedCategoryId en lugar de value si 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.jsx

Opció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.jsx

Regla 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 ...List y un ...Item.
  • Extrae componentes de estado visual: EmptyState, LoadingState, ErrorState.
  • Reduce props con objetos de dominio: pasar product completo 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: Tabs podría ser UI genérica; ReviewItem es 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?

Ahora responde el ejercicio sobre el contenido:

Al descomponer una pantalla en React, ¿cuál es la mejor forma de diferenciar un componente de presentación de uno contenedor?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

La separación se basa en la responsabilidad: presentación = mostrar UI según props; contenedor = conectar datos y acciones, coordinar estado y decidir qué se renderiza/pasa como props.

Siguiente capítulo

JSX en React: declarar interfaces con expresividad y reglas claras

Arrow Right Icon
Portada de libro electrónico gratuitaReact para principiantes: mentalidad de componentes y manejo de estado
8%

React para principiantes: mentalidad de componentes y manejo de estado

Nuevo curso

12 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.