Cerraste el Módulo 1 sabiendo cómo React convierte tus funciones en pantalla: JSX se transforma en objetos, esos objetos forman un árbol y la reconciliación decide qué tocar del DOM real. Ahora toca subir un nivel y mirar el problema desde el diseño, no desde el motor. Un componente no es solo «una función que devuelve JSX»: es la unidad de diseño de tu aplicación, la caja en la que decides qué marcado, qué lógica y qué estilo viven juntos. Esta lección trata de la decisión más frecuente y más determinante que tomarás en React: dónde poner las líneas divisorias. Partiremos del boceto real de la pantalla de catálogo de CicloUrbano, la trocearemos en componentes con criterio, dibujaremos su jerarquía y organizaremos los ficheros para que dentro de veinte componentes sigas encontrando lo que buscas.
Contenido
- Qué encapsula realmente un componente
- Del boceto a los componentes: la pantalla de catálogo de CicloUrbano
- Tres criterios para decidir los límites de un componente
- El árbol de componentes: relación padre e hijo
- Componentes de presentación y componentes contenedores
- Organización de ficheros y convenciones de nombres
- Exportación por defecto frente a exportación nombrada
- Componer: montar la pantalla completa
- El límite que arrastramos: los datos siguen fijos
- Qué encapsula realmente un componente
En el Módulo 1 escribiste Bienvenida y TarjetaBicicleta como funciones que devuelven JSX. Esa es la mecánica. La idea de fondo es otra: un componente agrupa en un solo sitio las tres cosas que definen un trozo de interfaz.
| Qué encapsula | En CicloUrbano, dentro de TarjetaBicicleta |
|---|---|
| Marcado | El <article>, el <h3> con el modelo, los <p> con tipo, estado y precio |
| Lógica | Cómo se formatea el precio, qué texto corresponde a cada estado, si el botón de reservar se muestra o no |
| Estilo | La clase tarjeta-bicicleta y las variantes que dependen del tipo de bicicleta |
Durante años la buena práctica en desarrollo web fue justo la contraria: HTML en un fichero, CSS en otro, JavaScript en un tercero. React propone separar por preocupación funcional en lugar de por tecnología. El argumento es sencillo: cuando te piden «cambia cómo se ve una bicicleta en el catálogo», no tocas «el CSS» ni «el JavaScript», tocas la tarjeta de bicicleta. Que todo lo que define la tarjeta esté a la vista en un mismo fichero es lo que hace el cambio rápido y seguro.
Esto tiene tres consecuencias prácticas que conviene interiorizar desde ya:
- Un componente es reemplazable. Si
TarjetaBicicletacumple su contrato, puedes reescribirla entera sin que nada del resto de la aplicación se entere. - Un componente es reutilizable. El mismo componente sirve en el catálogo, en el detalle de una estación y en la pantalla de reservas.
- Un componente es razonable en aislamiento. Puedes entender qué hace
TarjetaBicicletasin leerApp.jsx. Cuando esto deja de ser cierto, casi siempre es porque el componente hace demasiado.
- Del boceto a los componentes: la pantalla de catálogo de CicloUrbano
Este es el boceto de la pantalla que vamos a construir durante los próximos módulos: el catálogo de bicicletas de CicloUrbano.
┌──────────────────────────────────────────────────────────┐ │ CicloUrbano │ │ Alquila una bicicleta urbana en tu barrio… │ │ Catálogo · Estaciones · Mis reservas │ ├──────────────────────────────────────────────────────────┤ │ Bicicletas disponibles │ │ ┌────────────────────────────────────────────────────┐ │ │ │ Urbana Clásica [ disponible ] │ │ │ │ urbana · Plaza Mayor · 2,50 € / hora │ │ │ └────────────────────────────────────────────────────┘ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ Eléctrica Pro [ alquilada ] │ │ │ │ electrica · Plaza Mayor · 4,00 € / hora │ │ │ └────────────────────────────────────────────────────┘ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ Carga Max [ mantenimiento ] │ │ │ │ carga · Parque Norte · 5,50 € / hora │ │ │ └────────────────────────────────────────────────────┘ │ ├──────────────────────────────────────────────────────────┤ │ CicloUrbano — Servicio de alquiler de bicicletas │ │ Estaciones: Plaza Mayor · Parque Norte · Est. Central │ └──────────────────────────────────────────────────────────┘
El método para trocearlo es siempre el mismo: rodea con un rectángulo cada zona que tenga un nombre propio y comprueba si ese nombre describe una responsabilidad. Aplicándolo salen cinco componentes:
| Componente | Zona del boceto | Responsabilidad |
|---|---|---|
Cabecera |
Franja superior | Identidad del sitio y navegación principal |
ListaBicicletas |
Sección central | Presentar el conjunto de bicicletas con su título |
TarjetaBicicleta |
Cada recuadro | Mostrar una bicicleta |
EtiquetaEstado |
El distintivo [ disponible ] |
Traducir un estado a un distintivo visual |
PieDePagina |
Franja inferior | Información legal y de contacto |
Fíjate en dos decisiones que no son obvias:
ListaBicicletasexiste aunque de momento parezca un simple contenedor. Le damos entidad propia porque más adelante acogerá los filtros por tipo y estado y el mensaje de «no hay resultados». Es la zona que va a crecer, y tener ya su caja evita que ese crecimiento se derrame sobreApp.EtiquetaEstadoexiste aunque sea solo un<span>. El distintivo aparecerá también en el detalle de la estación y en el listado de reservas, y siempre con las mismas reglas de color. Un componente de dos líneas que se usa en tres sitios está más que justificado.
- Tres criterios para decidir los límites de un componente
No hay una regla mecánica, pero sí tres criterios que, aplicados en este orden, resuelven casi todos los casos.
Criterio 1: responsabilidad única
Un componente debería poder describirse en una frase sin la palabra «y».
- ✅ «
TarjetaBicicletamuestra los datos de una bicicleta.» - ❌ «
Catalogomuestra la cabecera y filtra las bicicletas y pinta las tarjetas y gestiona el pie.»
Cuando la descripción necesita conjunciones, tienes un candidato a división. Ojo con el falso positivo contrario: dividir tanto que cada componente sea una etiqueta HTML disfrazada. Un <TituloSeccion> que solo devuelve <h2> no añade nada.
Criterio 2: reutilización
Si un fragmento aparece —o va a aparecer— en más de un sitio con la misma pinta y las mismas reglas, es un componente. Este criterio es el que justifica EtiquetaEstado: el mapeo de disponible a verde y de mantenimiento a rojo es una decisión que queremos escrita una sola vez. Si estuviera copiada en tres pantallas, el día que cambie la paleta habrá que acordarse de los tres sitios.
Cuidado con el reverso: no crees un componente reutilizable antes de tener el segundo uso. Diseñar para una reutilización imaginaria produce componentes con contratos retorcidos que al final no encajan en el segundo caso real.
Criterio 3: tamaño manejable
No hay un número mágico de líneas, pero sí señales fiables de que un componente se ha pasado de tamaño:
| Señal de alarma | Qué suele indicar |
|---|---|
| No cabe en una pantalla del editor | Hace más de una cosa |
| El JSX anida más de cuatro o cinco niveles | Hay una sección interior que merece su propio componente |
| Cuesta ponerle nombre | Los límites están mal trazados |
| Al cambiar algo tocas partes que no tenían relación | Responsabilidades mezcladas |
Y una señal de que has dividido demasiado: para entender una pantalla sencilla tienes que abrir siete ficheros y saltar entre ellos. La fragmentación excesiva también es deuda técnica.
Un cuarto criterio que llegará más adelante
Cuando estudies el estado (lección State) aparecerá un criterio adicional muy potente: lo que cambia junto, vive junto. Las partes de la interfaz que se actualizan a la vez suelen pertenecer al mismo componente. De momento quédate con los tres primeros.
- El árbol de componentes: relación padre e hijo
Cuando un componente usa a otro dentro de su JSX, se establece una relación padre → hijo. El conjunto de todas esas relaciones forma el árbol de componentes, que es el mapa mental de tu aplicación.
flowchart TD
APP[App] --> CAB[Cabecera]
APP --> LIS[ListaBicicletas]
APP --> PIE[PieDePagina]
CAB --> BIE[Bienvenida]
LIS --> T1[TarjetaBicicleta]
LIS --> T2[TarjetaBicicleta]
LIS --> T3[TarjetaBicicleta]
T1 --> E1[EtiquetaEstado]
T2 --> E2[EtiquetaEstado]
T3 --> E3[EtiquetaEstado]
Tres precisiones importantes sobre este diagrama:
- Padre e hijo no es herencia.
TarjetaBicicletano hereda nada deListaBicicletas; simplemente aparece dentro de su JSX. React no usa herencia entre componentes, usa composición, y ese contraste se estudia a fondo en Composición vs Herencia. - Un mismo componente aparece tantas veces como se use. En el árbol hay tres nodos
TarjetaBicicletaporque hay tres bicicletas en pantalla. Cada uno es una instancia independiente: comparten el código, no los datos ni el estado. - La información baja por el árbol. El flujo de datos en React es unidireccional: de padre a hijo. Es exactamente el camino que recorrerán las props en la lección Props.
Este árbol es también lo que verás en la pestaña Components de React DevTools, la extensión que instalaste en la lección 01-02. Ábrela mientras construyes: comparar el árbol que has dibujado con el que React muestra es la forma más rápida de detectar que tu estructura mental y tu código se han separado.
- Componentes de presentación y componentes contenedores
Con el tiempo notarás que tus componentes caen de forma natural en dos familias.
| Componentes de presentación | Componentes contenedores | |
|---|---|---|
| Para qué sirven | Pintar algo | Decidir qué se pinta |
| De dónde sacan los datos | Se los dan desde fuera | Los obtienen, filtran u ordenan |
| Tienen estado | Normalmente no | Con frecuencia sí |
| Reutilización | Alta: sirven en cualquier pantalla | Baja: son específicos de un caso |
| Facilidad de prueba | Muy alta: entrada → salida | Menor: hay que simular datos y lógica |
| En CicloUrbano | TarjetaBicicleta, EtiquetaEstado, PieDePagina |
ListaBicicletas, App |
La utilidad de la distinción es práctica, no dogmática: empuja la lógica hacia arriba y deja las hojas del árbol tontas. Un TarjetaBicicleta que no sabe de dónde vienen sus datos se puede colocar en cualquier pantalla, se puede probar con dos líneas y se puede rediseñar sin miedo.
Un matiz honesto: en la comunidad de React esta separación se aplicaba antes de forma muy estricta (se hablaba de componentes «tontos» e «inteligentes»). Con los hooks, la línea se ha vuelto más difusa y hoy se considera una guía, no una ley. Aun así, preguntarte «¿esto pinta o esto decide?» sigue siendo una de las mejores formas de aclarar un componente confuso.
- Organización de ficheros y convenciones de nombres
Con cinco componentes cualquier organización funciona. Con cincuenta, no. Estas son las convenciones que usaremos en todo el curso.
src/ ├── componentes/ │ ├── Bienvenida.jsx │ ├── Cabecera.jsx │ ├── EtiquetaEstado.jsx │ ├── ListaBicicletas.jsx │ ├── PieDePagina.jsx │ ├── TarjetaBicicleta.jsx │ └── TarjetaEstacion.jsx ├── datos/ │ └── dominio.js ├── App.jsx ├── index.css └── main.jsx
Las reglas, y el motivo de cada una:
| Regla | Ejemplo | Por qué |
|---|---|---|
| Nombre de componente en PascalCase | TarjetaBicicleta |
JSX distingue componentes de etiquetas HTML por la mayúscula inicial (lección 01-03) |
| Un componente por fichero, con el mismo nombre | TarjetaBicicleta.jsx |
Buscar el código de lo que ves en pantalla es inmediato |
Extensión .jsx si contiene JSX |
Cabecera.jsx |
Deja claro de un vistazo qué ficheros producen interfaz; dominio.js es .js porque solo tiene datos |
| Nombre por rol, no por aspecto | EtiquetaEstado, no SpanVerde |
El aspecto cambia; el papel que juega el componente, mucho menos |
| Nombres en español, como el resto del dominio | ListaBicicletas |
Coherencia con el proyecto; las APIs de React se quedan en inglés |
Sobre la estructura de carpetas hay dos escuelas: por tipo (componentes/, datos/, hooks/) y por funcionalidad (catalogo/, reservas/, estaciones/). Para una aplicación pequeña como la nuestra, la primera es más sencilla y es la que seguiremos. En el Módulo 11, cuando CicloUrbano tenga varias secciones, revisaremos si merece la pena migrar a la segunda.
- Exportación por defecto frente a exportación nombrada
Cada fichero de componente tiene que exportar algo para que otros puedan importarlo. Hay dos formas, y conviene entender bien la diferencia.
Exportación por defecto
// src/componentes/EtiquetaEstado.jsx
function EtiquetaEstado() {
return <span className="estado estado--disponible">disponible</span>;
}
export default EtiquetaEstado;// Al importarlo, el nombre lo eliges tú (aunque no deberías cambiarlo)
import EtiquetaEstado from './componentes/EtiquetaEstado.jsx';Hay como máximo una exportación por defecto por fichero. Es la convención dominante en React para «el componente principal de este fichero».
Exportación nombrada
// src/datos/dominio.js
export const bicicletas = [ /* ... */ ];
export const estaciones = [ /* ... */ ];// Al importarlas, los nombres deben coincidir exactamente y van entre llaves
import { bicicletas, estaciones } from '../datos/dominio.js';Puede haber tantas como quieras por fichero. Es la forma natural cuando un módulo expone varias cosas, como nuestro dominio.js.
Comparativa
| Por defecto | Nombrada | |
|---|---|---|
| Sintaxis de exportación | export default X; |
export const X = …; |
| Sintaxis de importación | import X from '…' |
import { X } from '…' |
| Cuántas por fichero | Una | Varias |
| ¿Se puede renombrar al importar? | Sí, libremente | Sí, con as: import { X as Y } |
| Autocompletado del editor | Peor: el editor no conoce el nombre | Mejor: el nombre es exacto |
| Detección de erratas | Mala: import Tarjeta from './TarjetaBicicleta.jsx' compila |
Buena: un nombre inexistente da error |
Convención del curso: exportación por defecto para componentes (un componente por fichero) y exportación nombrada para datos y funciones auxiliares. Es la mezcla más extendida en proyectos con Vite y la que verás en la mayoría del código real.
- Componer: montar la pantalla completa
Vamos a escribir los componentes nuevos y a ensamblar la pantalla. Recuerda: los datos siguen escritos a mano dentro de cada componente, igual que en la lección 01-03.
EtiquetaEstado
// src/componentes/EtiquetaEstado.jsx
function EtiquetaEstado() {
return <span className="estado estado--disponible">disponible</span>;
}
export default EtiquetaEstado;Dos líneas, y sin embargo es un componente legítimo: concentra la decisión de cómo se representa un estado. Cuando en la lección Props reciba el estado desde fuera, la clase estado--disponible pasará a calcularse y este componente será el único sitio del proyecto donde esa regla exista.
Cabecera
// src/componentes/Cabecera.jsx
import Bienvenida from './Bienvenida.jsx';
function Cabecera() {
return (
<header className="cabecera">
<Bienvenida />
<nav className="cabecera__nav">
<ul>
<li>Catálogo</li>
<li>Estaciones</li>
<li>Mis reservas</li>
</ul>
</nav>
</header>
);
}
export default Cabecera;Aquí ocurre algo nuevo: un componente que usa a otro componente. Cabecera importa Bienvenida exactamente igual que App la importaba. No hay ninguna jerarquía especial: cualquier componente puede usar a cualquier otro que importe.
Como Bienvenida ahora vive dentro de un <header>, hay que quitarle el suyo para no anidar dos cabeceras, que sería incorrecto semánticamente:
// src/componentes/Bienvenida.jsx (ajustado)
function Bienvenida() {
return (
<div className="bienvenida">
<h1>CicloUrbano</h1>
<p>Alquila una bicicleta urbana en tu barrio, por horas y sin complicaciones.</p>
</div>
);
}
export default Bienvenida;Este ajuste ilustra algo que verás continuamente: los límites de los componentes se renegocian. Bienvenida nació como la cabecera entera porque no había nada más; ahora que aparece la navegación, su papel se reduce al mensaje de bienvenida y Cabecera asume el marco. Refactorizar límites es trabajo normal, no un síntoma de haberlo hecho mal.
ListaBicicletas
// src/componentes/ListaBicicletas.jsx
import TarjetaBicicleta from './TarjetaBicicleta.jsx';
function ListaBicicletas() {
return (
<section className="lista-bicicletas">
<h2>Bicicletas disponibles</h2>
<TarjetaBicicleta />
<TarjetaBicicleta />
<TarjetaBicicleta />
</section>
);
}
export default ListaBicicletas;Este componente es un contenedor en formación: hoy solo agrupa, pero es la caja preparada para recibir los filtros del Módulo 3.
TarjetaBicicleta, ahora con EtiquetaEstado dentro
// src/componentes/TarjetaBicicleta.jsx
import EtiquetaEstado from './EtiquetaEstado.jsx';
function TarjetaBicicleta() {
return (
<article className="tarjeta-bicicleta tarjeta-bicicleta--urbana">
<h3>
Urbana Clásica <EtiquetaEstado />
</h3>
<p>Tipo: urbana</p>
<p>Estación: Plaza Mayor</p>
<p>
<strong>2,50 € / hora</strong>
</p>
</article>
);
}
export default TarjetaBicicleta;Observa la clase tarjeta-bicicleta--urbana: la variante por tipo que fijamos en el diseño de CicloUrbano. En la lección Estilos dejará de estar escrita a mano y pasará a construirse a partir del tipo.
App: el ensamblaje
// src/App.jsx
import Cabecera from './componentes/Cabecera.jsx';
import ListaBicicletas from './componentes/ListaBicicletas.jsx';
import PieDePagina from './componentes/PieDePagina.jsx';
function App() {
return (
<>
<Cabecera />
<main>
<ListaBicicletas />
</main>
<PieDePagina />
</>
);
}
export default App;Mira lo que ha pasado con App: se ha quedado en tres líneas de JSX que se leen como un índice de la pantalla. Ese es el objetivo. Un componente raíz que se lee como un esquema es la mejor señal de que la descomposición está bien hecha; un App.jsx de doscientas líneas es la señal contraria.
Añade los estilos de las piezas nuevas a src/index.css:
/* Añadir al final de src/index.css */
.cabecera {
border-bottom: 1px solid #d9e2ec;
margin-bottom: 1.5rem;
}
.cabecera__nav ul {
display: flex;
gap: 1rem;
list-style: none;
padding: 0;
margin: 0 0 1rem;
color: #12805c;
font-weight: 600;
}
.lista-bicicletas {
margin-bottom: 2rem;
}
.estado {
display: inline-block;
padding: 0.15rem 0.6rem;
border-radius: 999px;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
vertical-align: middle;
color: #ffffff;
}
.estado--disponible { background-color: #12805c; }
.estado--alquilada { background-color: #b45309; }
.estado--mantenimiento { background-color: #9b1c1c; }
.tarjeta-bicicleta--urbana { border-left: 4px solid #12805c; }
.tarjeta-bicicleta--electrica { border-left: 4px solid #b45309; }
.tarjeta-bicicleta--carga { border-left: 4px solid #9b1c1c; }
- El límite que arrastramos: los datos siguen fijos
Abre el navegador y verás la pantalla del boceto… con tres bicicletas idénticas, las tres «Urbana Clásica, disponible, Plaza Mayor». La estructura es correcta; los datos, no.
La causa es la misma que en la lección 01-03: cada componente lleva sus datos escritos dentro. Y esto es exactamente lo contrario de lo que queremos, porque rompe el criterio de reutilización: un componente cuyos datos están dentro solo sirve para mostrar esos datos.
flowchart LR
A["Hoy: datos DENTRO del componente<br/>3 instancias = 3 tarjetas iguales"] --> B["Con props: datos DESDE FUERA<br/>3 instancias = 3 bicicletas distintas"]
La pieza que falta son las props, y es el contenido de la lección Props: Pasando Datos a Componentes. Allí TarjetaBicicleta recibirá una bicicleta del dominio.js y las tres tarjetas mostrarán por fin bici-001, bici-002 y bici-003. Pintar el array completo sin escribir las tarjetas una a una llegará algo después, en Listas y Claves.
Errores Comunes y Consejos
- Crear componentes «por si acaso». Extraer
TituloSeccion,ParrafoGrisoContenedorFlexantes de tener un problema real solo añade indirección. Extrae cuando el componente tenga un nombre de dominio evidente o cuando aparezca el segundo uso. - Dejar que
App.jsxcrezca sin control. Es el vertedero natural de la aplicación. SiAppempieza a contener marcado detallado en vez de una lista de secciones, extrae ese marcado a un componente. - Definir un componente dentro de otro. Escribir
function TarjetaBicicleta() {…}dentro del cuerpo deListaBicicletascompila, pero en cada render se crea una función nueva; para la reconciliación que estudiaste en 01-05 es un tipo distinto, así que destruye y recrea el subárbol entero y pierde su estado. Define siempre los componentes en el nivel superior del módulo. - Nombrar por aspecto en lugar de por rol.
CajaVerdedeja de tener sentido el día que el diseño cambia a azul.EtiquetaEstadosigue siendo correcto pase lo que pase con la paleta. - Confundir el árbol de componentes con el árbol del DOM.
Cabecerano genera ninguna etiqueta<cabecera>: genera lo que hay en sureturn. Un componente puede producir muchos nodos, uno o ninguno. - Consejo: dibuja el árbol antes de escribir código. Cinco minutos con un boceto y un rotulador ahorran una tarde de reorganización. Y compáralo después con la pestaña Components de React DevTools.
- Consejo: aplica la prueba del nombre. Si no consigues ponerle a un componente un nombre corto y descriptivo, el problema no es el nombre: son sus límites.
Ejercicios
Ejercicio 1
La aplicación de CicloUrbano necesita una pantalla de detalle de estación. El boceto es este:
Plaza Mayor · Barrio Centro 20 plazas · 12 libres ──────────────────────────────── Bicicletas en esta estación [ tarjeta de bicicleta ] [ tarjeta de bicicleta ]
Identifica los componentes que emplearías, indica cuáles ya existen y cuáles habría que crear, y clasifica cada uno como de presentación o contenedor. Justifica cada decisión con uno de los tres criterios del apartado 3.
Ejercicio 2
Un compañero ha escrito este componente. Detecta los problemas de diseño y propón una descomposición mejor, sin escribir el código completo: basta con el árbol resultante y el nombre y la responsabilidad de cada pieza.
// src/componentes/Todo.jsx
function Todo() {
return (
<div>
<h1>CicloUrbano</h1>
<p>Alquila una bicicleta urbana en tu barrio…</p>
<div><span>Catálogo</span> <span>Estaciones</span></div>
<h2>Bicicletas disponibles</h2>
<div className="tarjeta-bicicleta">
<h3>Urbana Clásica <span className="estado estado--disponible">disponible</span></h3>
<p>2,50 € / hora</p>
</div>
<div className="tarjeta-bicicleta">
<h3>Eléctrica Pro <span className="estado estado--alquilada">alquilada</span></h3>
<p>4,00 € / hora</p>
</div>
<footer><p>CicloUrbano — Servicio de alquiler de bicicletas urbanas</p></footer>
</div>
);
}Ejercicio 3
Crea el componente ResumenFlota en src/componentes/ResumenFlota.jsx. Debe mostrar, dentro de una <section className="resumen-flota">, un <h2> con el texto «Resumen de la flota» y tres líneas con los totales de la flota de CicloUrbano según el dominio.js: total de bicicletas, disponibles y no disponibles. Escribe los números a mano (todavía no sabemos calcularlos desde los datos) y colócalo en App.jsx entre la cabecera y la lista. Después dibuja el árbol de componentes actualizado.
Soluciones
Solución 1.
| Componente | ¿Existe? | Tipo | Criterio que lo justifica |
|---|---|---|---|
DetalleEstacion |
Nuevo | Contenedor | Responsabilidad única: coordina la pantalla; es la caja donde vivirá la lógica de la estación |
TarjetaEstacion |
Ya existe (01-03) | Presentación | Reutilización: la cabecera con nombre, barrio y plazas es la misma que en el listado de estaciones |
DisponibilidadEstacion |
Nuevo | Presentación | Responsabilidad única: «20 plazas · 12 libres» es una información con reglas propias que también aparecerá en el mapa |
ListaBicicletas |
Ya existe | Contenedor | Reutilización: exactamente el mismo bloque del catálogo, filtrado por estación |
TarjetaBicicleta |
Ya existe | Presentación | Reutilización: es el mismo componente sin ningún cambio |
EtiquetaEstado |
Ya existe | Presentación | Reutilización: tercer uso del distintivo |
Lo relevante del ejercicio es que cuatro de los seis componentes ya estaban escritos. Eso es lo que se gana con unos límites bien trazados: la segunda pantalla cuesta mucho menos que la primera.
Solución 2.
Problemas de diseño:
- Sin responsabilidad única.
Todohace de cabecera, navegación, catálogo, tarjeta y pie a la vez; su descripción necesitaría cuatro «y». - Cero reutilización. El marcado de la tarjeta está duplicado literalmente, y el del distintivo de estado también. Cualquier cambio de diseño hay que aplicarlo dos veces.
- Nombre inútil.
Todono describe ninguna responsabilidad, lo cual es el síntoma clásico de límites mal trazados. - Marcado no semántico.
<div>para las tarjetas y para la navegación, en lugar de<article>y<nav>.
Descomposición propuesta:
flowchart TD
APP[App] --> CAB[Cabecera]
APP --> LIS[ListaBicicletas]
APP --> PIE[PieDePagina]
CAB --> BIE[Bienvenida]
LIS --> T1[TarjetaBicicleta]
LIS --> T2[TarjetaBicicleta]
T1 --> E1[EtiquetaEstado]
T2 --> E2[EtiquetaEstado]
| Componente | Responsabilidad |
|---|---|
App |
Ensamblar las tres zonas de la pantalla |
Cabecera |
Identidad del sitio y navegación |
Bienvenida |
Título y eslogan |
ListaBicicletas |
Título de la sección y conjunto de tarjetas |
TarjetaBicicleta |
Datos de una bicicleta |
EtiquetaEstado |
Distintivo visual del estado |
PieDePagina |
Información de cierre |
Solución 3.
// src/componentes/ResumenFlota.jsx
function ResumenFlota() {
return (
<section className="resumen-flota">
<h2>Resumen de la flota</h2>
<p>Total de bicicletas: 5</p>
<p>Disponibles: 3</p>
<p>No disponibles: 2</p>
</section>
);
}
export default ResumenFlota;// src/App.jsx
import Cabecera from './componentes/Cabecera.jsx';
import ResumenFlota from './componentes/ResumenFlota.jsx';
import ListaBicicletas from './componentes/ListaBicicletas.jsx';
import PieDePagina from './componentes/PieDePagina.jsx';
function App() {
return (
<>
<Cabecera />
<main>
<ResumenFlota />
<ListaBicicletas />
</main>
<PieDePagina />
</>
);
}
export default App;Árbol actualizado:
flowchart TD
APP[App] --> CAB[Cabecera]
APP --> RES[ResumenFlota]
APP --> LIS[ListaBicicletas]
APP --> PIE[PieDePagina]
CAB --> BIE[Bienvenida]
LIS --> T1[TarjetaBicicleta]
LIS --> T2[TarjetaBicicleta]
LIS --> T3[TarjetaBicicleta]
T1 --> E1[EtiquetaEstado]
T2 --> E2[EtiquetaEstado]
T3 --> E3[EtiquetaEstado]
Los números «5, 3, 2» salen de contar a mano el dominio.js, y ese es precisamente el problema: si mañana entra una bicicleta nueva, el resumen mentirá. Derivar esos totales de los datos es cuestión de una lección.
Conclusión
Un componente es la unidad de diseño de React: encapsula marcado, lógica y estilo de un trozo de interfaz, y su valor depende sobre todo de dónde traces sus límites. Ahora tienes un método para trazarlos: partir del boceto, rodear las zonas con nombre propio y validar cada candidato contra tres criterios —responsabilidad única, reutilización y tamaño manejable—, evitando tanto el componente que lo hace todo como la fragmentación en piezas triviales.
Has convertido el boceto del catálogo de CicloUrbano en un árbol de componentes real: App ensambla Cabecera, ListaBicicletas y PieDePagina; Cabecera contiene Bienvenida; ListaBicicletas contiene TarjetaBicicleta, y esta contiene EtiquetaEstado. Sabes distinguir componentes de presentación de componentes contenedores y usar esa distinción para empujar la lógica hacia arriba y dejar las hojas reutilizables. Y tienes fijadas las convenciones que seguirá todo el curso: un componente por fichero en src/componentes/, PascalCase, extensión .jsx, exportación por defecto para componentes y nombrada para datos.
También has visto el techo con el que chocamos: la estructura es correcta pero las tres tarjetas son idénticas, porque los datos siguen escritos dentro de los componentes.
Antes de romper ese techo, hay una parada obligatoria. Todo lo que has escrito son componentes de función, pero en cualquier proyecto React con algunos años de vida te encontrarás componentes escritos con clases: otra sintaxis, con this, render() y constructor. No vas a escribir código nuevo así, pero sí necesitas saber leerlo. Ese es el tema de la siguiente lección: Componentes Funcionales vs de Clase.
Curso de React
Módulo 1: Introducción a React
- ¿Qué es React?
- Configuración del Entorno de Desarrollo
- Hola Mundo en React
- JSX: Extensión de Sintaxis de JavaScript
- Cómo Renderiza React: Virtual DOM y Reconciliación
Módulo 2: Componentes de React
- Entendiendo los Componentes
- Componentes Funcionales vs de Clase
- Props: Pasando Datos a Componentes
- State: Gestión del Estado del Componente
- Estilos en los Componentes: CSS, Módulos y Utilidades
Módulo 3: Trabajando con Eventos
- Manejo de Eventos en React
- Renderizado Condicional
- Listas y Claves
- Formularios y Componentes Controlados
- Validación de Formularios y Componentes No Controlados
- Accesibilidad en Componentes Interactivos
Módulo 4: Conceptos Avanzados de Componentes
- Elevando el Estado
- Composición vs Herencia
- Métodos del Ciclo de Vida de React
- Hooks: Introducción y Uso Básico
- Límites de Error: Capturar Fallos en la Interfaz
Módulo 5: Hooks de React
- Hook useState
- Hook useEffect
- Hook useRef y Acceso al DOM
- Hook useContext
- Hook useReducer
- Hooks Personalizados
Módulo 6: Enrutamiento en React
- Introducción a React Router
- Configuración de React Router
- Rutas Anidadas
- Navegación Programática
- Rutas Protegidas y Control de Acceso
Módulo 7: Gestión del Estado
- Introducción a la Gestión del Estado
- API de Contexto
- Redux: Introducción y Configuración
- Redux: Acciones y Reductores
- Redux: Conectando a React
- Estado del Servidor: Peticiones, Caché y Sincronización
Módulo 8: Optimización del Rendimiento
- Técnicas de Optimización del Rendimiento en React
- Memorización con React.memo
- Hooks useMemo y useCallback
- División de Código y Carga Perezosa
- Medir el Rendimiento con React DevTools Profiler
Módulo 9: Pruebas en React
- Introducción a las Pruebas
- Pruebas Unitarias con Jest
- Pruebas de Componentes con React Testing Library
- Pruebas de Código Asíncrono y Simulación de APIs
- Pruebas de Extremo a Extremo con Cypress
Módulo 10: Temas Avanzados
- Renderizado del Lado del Servidor (SSR) con Next.js
- Generación de Sitios Estáticos (SSG) con Next.js
- Suspense y React Server Components
- TypeScript con React
- React Native: Creación de Aplicaciones Móviles
