Vas tancar el Mòdul 1 sabent com React converteix les teves funcions en pantalla: JSX es transforma en objectes, aquests objectes formen un arbre i la reconciliació decideix què s'ha de tocar del DOM real. Ara toca pujar un nivell i mirar el problema des del disseny, no des del motor. Un component no és només «una funció que retorna JSX»: és la unitat de disseny de la teva aplicació, la caixa en què decideixes quin marcatge, quina lògica i quin estil viuen junts. Aquesta lliçó tracta de la decisió més freqüent i més determinant que prendràs a React: on posar les línies divisòries. Partirem de l'esbós real de la pantalla de catàleg de CicloUrbano, la trossejarem en components amb criteri, dibuixarem la seva jerarquia i organitzarem els fitxers perquè dins de vint components continuïs trobant el que busques.

Contingut

  1. Què encapsula realment un component
  2. De l'esbós als components: la pantalla de catàleg de CicloUrbano
  3. Tres criteris per decidir els límits d'un component
  4. L'arbre de components: relació pare i fill
  5. Components de presentació i components contenidors
  6. Organització de fitxers i convencions de noms
  7. Exportació per defecte enfront de l'exportació amb nom
  8. Compondre: muntar la pantalla completa
  9. El límit que arrosseguem: les dades continuen fixes

  1. Què encapsula realment un component

Al Mòdul 1 vas escriure Benvinguda i TargetaBicicleta com a funcions que retornen JSX. Aquesta és la mecànica. La idea de fons és una altra: un component agrupa en un sol lloc les tres coses que defineixen un tros d'interfície.

Què encapsula A CicloUrbano, dins de TargetaBicicleta
Marcatge L'<article>, l'<h3> amb el model, els <p> amb tipus, estat i preu
Lògica Com es formata el preu, quin text correspon a cada estat, si el botó de reservar es mostra o no
Estil La classe targeta-bicicleta i les variants que depenen del tipus de bicicleta

Durant anys la bona pràctica en desenvolupament web va ser justament la contrària: HTML en un fitxer, CSS en un altre, JavaScript en un tercer. React proposa separar per preocupació funcional en lloc de per tecnologia. L'argument és senzill: quan et demanen «canvia com es veu una bicicleta al catàleg», no toques «el CSS» ni «el JavaScript», toques la targeta de bicicleta. Que tot el que defineix la targeta estigui a la vista en un mateix fitxer és el que fa que el canvi sigui ràpid i segur.

Això té tres conseqüències pràctiques que convé interioritzar des d'ara:

  • Un component és reemplaçable. Si TargetaBicicleta compleix el seu contracte, pots reescriure-la sencera sense que res de la resta de l'aplicació se n'assabenti.
  • Un component és reutilitzable. El mateix component serveix al catàleg, al detall d'una estació i a la pantalla de reserves.
  • Un component és raonable en aïllament. Pots entendre què fa TargetaBicicleta sense llegir App.jsx. Quan això deixa de ser cert, gairebé sempre és perquè el component fa massa coses.

  1. De l'esbós als components: la pantalla de catàleg de CicloUrbano

Aquest és l'esbós de la pantalla que construirem durant els pròxims mòduls: el catàleg de bicicletes de CicloUrbano.

┌──────────────────────────────────────────────────────────┐
│  CicloUrbano                                              │
│  Lloga una bicicleta urbana al teu barri…                 │
│  Catàleg · Estacions · Les meves reserves                 │
├──────────────────────────────────────────────────────────┤
│  Bicicletes disponibles                                   │
│  ┌────────────────────────────────────────────────────┐   │
│  │ Urbana Clàssica                    [ disponible ]  │   │
│  │ urbana · Plaça Major · 2,50 € / hora                │   │
│  └────────────────────────────────────────────────────┘   │
│  ┌────────────────────────────────────────────────────┐   │
│  │ Elèctrica Pro                      [ llogada    ]  │   │
│  │ electrica · Plaça Major · 4,00 € / hora             │   │
│  └────────────────────────────────────────────────────┘   │
│  ┌────────────────────────────────────────────────────┐   │
│  │ Càrrega Max                      [ manteniment ]   │   │
│  │ carga · Parc Nord · 5,50 € / hora                   │   │
│  └────────────────────────────────────────────────────┘   │
├──────────────────────────────────────────────────────────┤
│  CicloUrbano — Servei de lloguer de bicicletes             │
│  Estacions: Plaça Major · Parc Nord · Est. Central         │
└──────────────────────────────────────────────────────────┘

El mètode per trossejar-lo és sempre el mateix: envolta amb un rectangle cada zona que tingui un nom propi i comprova si aquest nom descriu una responsabilitat. Aplicant-lo en surten cinc components:

Component Zona de l'esbós Responsabilitat
Capcalera Franja superior Identitat del lloc i navegació principal
LlistaBicicletes Secció central Presentar el conjunt de bicicletes amb el seu títol
TargetaBicicleta Cada requadre Mostrar una bicicleta
EtiquetaEstat El distintiu [ disponible ] Traduir un estat a un distintiu visual
PeuDePagina Franja inferior Informació legal i de contacte

Fixa't en dues decisions que no són òbvies:

  • LlistaBicicletes existeix encara que de moment sembli un simple contenidor. Li donem entitat pròpia perquè més endavant acollirà els filtres per tipus i estat i el missatge de «no hi ha resultats». És la zona que creixerà, i tenir ja la seva caixa evita que aquest creixement es vessi sobre App.
  • EtiquetaEstat existeix encara que només sigui un <span>. El distintiu apareixerà també al detall de l'estació i al llistat de reserves, i sempre amb les mateixes regles de color. Un component de dues línies que es fa servir en tres llocs està més que justificat.

  1. Tres criteris per decidir els límits d'un component

No hi ha una regla mecànica, però sí tres criteris que, aplicats en aquest ordre, resolen gairebé tots els casos.

Criteri 1: responsabilitat única

Un component hauria de poder-se descriure en una frase sense la paraula «i».

  • ✅ «TargetaBicicleta mostra les dades d'una bicicleta.»
  • ❌ «Cataleg mostra la capçalera i filtra les bicicletes i pinta les targetes i gestiona el peu.»

Quan la descripció necessita conjuncions, tens un candidat a divisió. Compte amb el fals positiu contrari: dividir tant que cada component sigui una etiqueta HTML disfressada. Un <TitolSeccio> que només retorna <h2> no afegeix res.

Criteri 2: reutilització

Si un fragment apareix —o apareixerà— en més d'un lloc amb el mateix aspecte i les mateixes regles, és un component. Aquest criteri és el que justifica EtiquetaEstat: el mapatge de disponible a verd i de mantenimiento a vermell és una decisió que volem escrita una sola vegada. Si estigués copiada en tres pantalles, el dia que canviï la paleta caldrà recordar-se dels tres llocs.

Compte amb el revers: no creïs un component reutilitzable abans de tenir el segon ús. Dissenyar per a una reutilització imaginària produeix components amb contractes retorçats que al final no encaixen en el segon cas real.

Criteri 3: mida manejable

No hi ha un número màgic de línies, però sí senyals fiables que un component s'ha passat de mida:

Senyal d'alarma Què sol indicar
No cap en una pantalla de l'editor Fa més d'una cosa
El JSX s'imbrica més de quatre o cinc nivells Hi ha una secció interior que mereix el seu propi component
Costa posar-li nom Els límits estan mal traçats
En canviar alguna cosa toques parts que no tenien relació Responsabilitats barrejades

I un senyal que has dividit massa: per entendre una pantalla senzilla has d'obrir set fitxers i saltar entre ells. La fragmentació excessiva també és deute tècnic.

Un quart criteri que arribarà més endavant

Quan estudiïs l'estat (lliçó State) apareixerà un criteri addicional molt potent: el que canvia junt, viu junt. Les parts de la interfície que s'actualitzen alhora solen pertànyer al mateix component. De moment queda't amb els tres primers.

  1. L'arbre de components: relació pare i fill

Quan un component fa servir un altre dins del seu JSX, s'estableix una relació pare → fill. El conjunt de totes aquestes relacions forma l'arbre de components, que és el mapa mental de la teva aplicació.

flowchart TD
    APP[App] --> CAB[Capcalera]
    APP --> LIS[LlistaBicicletes]
    APP --> PIE[PeuDePagina]
    CAB --> BIE[Benvinguda]
    LIS --> T1[TargetaBicicleta]
    LIS --> T2[TargetaBicicleta]
    LIS --> T3[TargetaBicicleta]
    T1 --> E1[EtiquetaEstat]
    T2 --> E2[EtiquetaEstat]
    T3 --> E3[EtiquetaEstat]

Tres precisions importants sobre aquest diagrama:

  • Pare i fill no és herència. TargetaBicicleta no hereta res de LlistaBicicletes; simplement apareix dins del seu JSX. React no fa servir herència entre components, fa servir composició, i aquest contrast s'estudia a fons a Composició vs Herència.
  • Un mateix component apareix tantes vegades com es faci servir. A l'arbre hi ha tres nodes TargetaBicicleta perquè hi ha tres bicicletes en pantalla. Cadascun és una instància independent: comparteixen el codi, no les dades ni l'estat.
  • La informació baixa per l'arbre. El flux de dades a React és unidireccional: de pare a fill. És exactament el camí que recorreran les props a la lliçó Props.

Aquest arbre és també el que veuràs a la pestanya Components de React DevTools, l'extensió que vas instal·lar a la lliçó 01-02. Obre-la mentre construeixes: comparar l'arbre que has dibuixat amb el que mostra React és la manera més ràpida de detectar que la teva estructura mental i el teu codi s'han separat.

  1. Components de presentació i components contenidors

Amb el temps notaràs que els teus components cauen de manera natural en dues famílies.

Components de presentació Components contenidors
Per a què serveixen Pintar alguna cosa Decidir què es pinta
D'on treuen les dades Els les donen des de fora Les obtenen, filtren o ordenen
Tenen estat Normalment no Sovint sí
Reutilització Alta: serveixen en qualsevol pantalla Baixa: són específics d'un cas
Facilitat de prova Molt alta: entrada → sortida Menor: cal simular dades i lògica
A CicloUrbano TargetaBicicleta, EtiquetaEstat, PeuDePagina LlistaBicicletes, App

La utilitat de la distinció és pràctica, no dogmàtica: empeny la lògica cap amunt i deixa les fulles de l'arbre beneites. Un TargetaBicicleta que no sap d'on vénen les seves dades es pot col·locar en qualsevol pantalla, es pot provar amb dues línies i es pot redissenyar sense por.

Un matís honest: a la comunitat de React aquesta separació s'aplicava abans de manera molt estricta (es parlava de components «beneits» i «intel·ligents»). Amb els hooks, la línia s'ha tornat més difusa i avui es considera una guia, no una llei. Tot i així, preguntar-te «això pinta o això decideix?» continua sent una de les millors maneres d'aclarir un component confús.

  1. Organització de fitxers i convencions de noms

Amb cinc components qualsevol organització funciona. Amb cinquanta, no. Aquestes són les convencions que farem servir en tot el curs.

src/
├── components/
│   ├── Benvinguda.jsx
│   ├── Capcalera.jsx
│   ├── EtiquetaEstat.jsx
│   ├── LlistaBicicletes.jsx
│   ├── PeuDePagina.jsx
│   ├── TargetaBicicleta.jsx
│   └── TargetaEstacio.jsx
├── dades/
│   └── domini.js
├── App.jsx
├── index.css
└── main.jsx

Les regles, i el motiu de cadascuna:

Regla Exemple Per què
Nom de component en PascalCase TargetaBicicleta JSX distingeix components d'etiquetes HTML per la majúscula inicial (lliçó 01-03)
Un component per fitxer, amb el mateix nom TargetaBicicleta.jsx Buscar el codi del que veus en pantalla és immediat
Extensió .jsx si conté JSX Capcalera.jsx Deixa clar d'una ullada quins fitxers produeixen interfície; domini.js és .js perquè només té dades
Nom per rol, no per aspecte EtiquetaEstat, no SpanVerd L'aspecte canvia; el paper que juga el component, molt menys
Noms en català, com la resta del domini LlistaBicicletes Coherència amb el projecte; les APIs de React es queden en anglès

Sobre l'estructura de carpetes hi ha dues escoles: per tipus (components/, dades/, hooks/) i per funcionalitat (cataleg/, reserves/, estacions/). Per a una aplicació petita com la nostra, la primera és més senzilla i és la que seguirem. Al Mòdul 11, quan CicloUrbano tingui diverses seccions, revisarem si val la pena migrar a la segona.

  1. Exportació per defecte enfront de l'exportació amb nom

Cada fitxer de component ha d'exportar alguna cosa perquè altres el puguin importar. Hi ha dues maneres, i convé entendre bé la diferència.

Exportació per defecte

// src/components/EtiquetaEstat.jsx
function EtiquetaEstat() {
  return <span className="estat estat--disponible">disponible</span>;
}

export default EtiquetaEstat;
// En importar-lo, el nom el tries tu (encara que no l'hauries de canviar)
import EtiquetaEstat from './components/EtiquetaEstat.jsx';

Hi ha com a màxim una exportació per defecte per fitxer. És la convenció dominant a React per a «el component principal d'aquest fitxer».

Exportació amb nom

// src/dades/domini.js
export const bicicletes = [ /* ... */ ];
export const estacions = [ /* ... */ ];
// En importar-les, els noms han de coincidir exactament i van entre claus
import { bicicletes, estacions } from '../dades/domini.js';

N'hi pot haver tantes com vulguis per fitxer. És la manera natural quan un mòdul exposa diverses coses, com el nostre domini.js.

Comparativa

Per defecte Amb nom
Sintaxi d'exportació export default X; export const X = …;
Sintaxi d'importació import X from '…' import { X } from '…'
Quantes per fitxer Una Diverses
Es pot reanomenar en importar? Sí, lliurement Sí, amb as: import { X as Y }
Autocompletat de l'editor Pitjor: l'editor no coneix el nom Millor: el nom és exacte
Detecció d'errates Dolenta: import Targeta from './TargetaBicicleta.jsx' compila Bona: un nom inexistent dona error

Convenció del curs: exportació per defecte per als components (un component per fitxer) i exportació amb nom per a dades i funcions auxiliars. És la barreja més estesa en projectes amb Vite i la que veuràs en la majoria del codi real.

  1. Compondre: muntar la pantalla completa

Escriurem els components nous i muntarem la pantalla. Recorda: les dades continuen escrites a mà dins de cada component, igual que a la lliçó 01-03.

EtiquetaEstat

// src/components/EtiquetaEstat.jsx
function EtiquetaEstat() {
  return <span className="estat estat--disponible">disponible</span>;
}

export default EtiquetaEstat;

Dues línies, i tot i així és un component legítim: concentra la decisió de com es representa un estat. Quan a la lliçó Props rebi l'estat des de fora, la classe estat--disponible passarà a calcular-se i aquest component serà l'únic lloc del projecte on aquesta regla existeixi.

Capcalera

// src/components/Capcalera.jsx
import Benvinguda from './Benvinguda.jsx';

function Capcalera() {
  return (
    <header className="capcalera">
      <Benvinguda />
      <nav className="capcalera__nav">
        <ul>
          <li>Catàleg</li>
          <li>Estacions</li>
          <li>Les meves reserves</li>
        </ul>
      </nav>
    </header>
  );
}

export default Capcalera;

Aquí passa una cosa nova: un component que fa servir un altre component. Capcalera importa Benvinguda exactament igual que App la importava. No hi ha cap jerarquia especial: qualsevol component pot fer servir qualsevol altre que importi.

Com que Benvinguda ara viu dins d'un <header>, cal treure-li el propi perquè no s'imbriquin dues capçaleres, cosa que seria incorrecta semànticament:

// src/components/Benvinguda.jsx  (ajustat)
function Benvinguda() {
  return (
    <div className="benvinguda">
      <h1>CicloUrbano</h1>
      <p>Lloga una bicicleta urbana al teu barri, per hores i sense complicacions.</p>
    </div>
  );
}

export default Benvinguda;

Aquest ajust il·lustra una cosa que veuràs contínuament: els límits dels components es renegocien. Benvinguda va néixer com la capçalera sencera perquè no hi havia res més; ara que apareix la navegació, el seu paper es redueix al missatge de benvinguda i Capcalera assumeix el marc. Refactoritzar límits és feina normal, no un símptoma d'haver-ho fet malament.

LlistaBicicletes

// src/components/LlistaBicicletes.jsx
import TargetaBicicleta from './TargetaBicicleta.jsx';

function LlistaBicicletes() {
  return (
    <section className="llista-bicicletes">
      <h2>Bicicletes disponibles</h2>
      <TargetaBicicleta />
      <TargetaBicicleta />
      <TargetaBicicleta />
    </section>
  );
}

export default LlistaBicicletes;

Aquest component és un contenidor en formació: avui només agrupa, però és la caixa preparada per rebre els filtres del Mòdul 3.

TargetaBicicleta, ara amb EtiquetaEstat a dins

// src/components/TargetaBicicleta.jsx
import EtiquetaEstat from './EtiquetaEstat.jsx';

function TargetaBicicleta() {
  return (
    <article className="targeta-bicicleta targeta-bicicleta--urbana">
      <h3>
        Urbana Clàssica <EtiquetaEstat />
      </h3>
      <p>Tipus: urbana</p>
      <p>Estació: Plaça Major</p>
      <p>
        <strong>2,50 € / hora</strong>
      </p>
    </article>
  );
}

export default TargetaBicicleta;

Observa la classe targeta-bicicleta--urbana: la variant per tipus que vam fixar en el disseny de CicloUrbano. A la lliçó Estils deixarà d'estar escrita a mà i passarà a construir-se a partir del tipus.

App: el muntatge

// src/App.jsx
import Capcalera from './components/Capcalera.jsx';
import LlistaBicicletes from './components/LlistaBicicletes.jsx';
import PeuDePagina from './components/PeuDePagina.jsx';

function App() {
  return (
    <>
      <Capcalera />
      <main>
        <LlistaBicicletes />
      </main>
      <PeuDePagina />
    </>
  );
}

export default App;

Mira què ha passat amb App: s'ha quedat en tres línies de JSX que es llegeixen com un índex de la pantalla. Aquest és l'objectiu. Un component arrel que es llegeix com un esquema és el millor senyal que la descomposició està ben feta; un App.jsx de dues-centes línies és el senyal contrari.

Afegeix els estils de les peces noves a src/index.css:

/* Afegir al final de src/index.css */
.capcalera {
  border-bottom: 1px solid #d9e2ec;
  margin-bottom: 1.5rem;
}

.capcalera__nav ul {
  display: flex;
  gap: 1rem;
  list-style: none;
  padding: 0;
  margin: 0 0 1rem;
  color: #12805c;
  font-weight: 600;
}

.llista-bicicletes {
  margin-bottom: 2rem;
}

.estat {
  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;
}

.estat--disponible { background-color: #12805c; }
.estat--alquilada { background-color: #b45309; }
.estat--mantenimiento { background-color: #9b1c1c; }

.targeta-bicicleta--urbana { border-left: 4px solid #12805c; }
.targeta-bicicleta--electrica { border-left: 4px solid #b45309; }
.targeta-bicicleta--carga { border-left: 4px solid #9b1c1c; }

  1. El límit que arrosseguem: les dades continuen fixes

Obre el navegador i veuràs la pantalla de l'esbós… amb tres bicicletes idèntiques, les tres «Urbana Clàssica, disponible, Plaça Major». L'estructura és correcta; les dades, no.

La causa és la mateixa que a la lliçó 01-03: cada component porta les seves dades escrites a dins. I això és exactament el contrari del que volem, perquè trenca el criteri de reutilització: un component les dades del qual estan a dins només serveix per mostrar aquestes dades.

flowchart LR
    A["Avui: dades DINS del component<br/>3 instàncies = 3 targetes iguals"] --> B["Amb props: dades DES DE FORA<br/>3 instàncies = 3 bicicletes diferents"]

La peça que falta són les props, i és el contingut de la lliçó Props: Passant Dades als Components. Allà TargetaBicicleta rebrà una bicicleta del domini.js i les tres targetes mostraran per fi bici-001, bici-002 i bici-003. Pintar l'array complet sense escriure les targetes una a una arribarà una mica després, a Llistes i Claus.

Errors Comuns i Consells

  • Crear components «per si de cas». Extreure TitolSeccio, ParagrafGris o ContenidorFlex abans de tenir un problema real només afegeix indirecció. Extreu quan el component tingui un nom de domini evident o quan aparegui el segon ús.
  • Deixar que App.jsx creixi sense control. És l'abocador natural de l'aplicació. Si App comença a contenir marcatge detallat en lloc d'una llista de seccions, extreu aquest marcatge a un component.
  • Definir un component dins d'un altre. Escriure function TargetaBicicleta() {…} dins del cos de LlistaBicicletes compila, però a cada render es crea una funció nova; per a la reconciliació que vas estudiar a 01-05 és un tipus diferent, així que destrueix i recrea el subarbre sencer i perd el seu estat. Defineix sempre els components al nivell superior del mòdul.
  • Anomenar per aspecte en lloc de per rol. CaixaVerda deixa de tenir sentit el dia que el disseny canvia a blau. EtiquetaEstat continua sent correcte passi el que passi amb la paleta.
  • Confondre l'arbre de components amb l'arbre del DOM. Capcalera no genera cap etiqueta <capcalera>: genera el que hi ha al seu return. Un component pot produir molts nodes, un o cap.
  • Consell: dibuixa l'arbre abans d'escriure codi. Cinc minuts amb un esbós i un retolador estalvien una tarda de reorganització. I compara'l després amb la pestanya Components de React DevTools.
  • Consell: aplica la prova del nom. Si no aconsegueixes posar-li a un component un nom curt i descriptiu, el problema no és el nom: són els seus límits.

Exercicis

Exercici 1

L'aplicació de CicloUrbano necessita una pantalla de detall d'estació. L'esbós és aquest:

Plaça Major  ·  Barri Centre
20 places · 12 lliures
────────────────────────────────
Bicicletes en aquesta estació
  [ targeta de bicicleta ]
  [ targeta de bicicleta ]

Identifica els components que faries servir, indica quins ja existeixen i quins caldria crear, i classifica cadascun com de presentació o contenidor. Justifica cada decisió amb un dels tres criteris de l'apartat 3.

Exercici 2

Un company ha escrit aquest component. Detecta els problemes de disseny i proposa una descomposició millor, sense escriure el codi complet: n'hi ha prou amb l'arbre resultant i el nom i la responsabilitat de cada peça.

// src/components/Tot.jsx
function Tot() {
  return (
    <div>
      <h1>CicloUrbano</h1>
      <p>Lloga una bicicleta urbana al teu barri…</p>
      <div><span>Catàleg</span> <span>Estacions</span></div>
      <h2>Bicicletes disponibles</h2>
      <div className="targeta-bicicleta">
        <h3>Urbana Clàssica <span className="estat estat--disponible">disponible</span></h3>
        <p>2,50 € / hora</p>
      </div>
      <div className="targeta-bicicleta">
        <h3>Elèctrica Pro <span className="estat estat--alquilada">llogada</span></h3>
        <p>4,00 € / hora</p>
      </div>
      <footer><p>CicloUrbano — Servei de lloguer de bicicletes urbanes</p></footer>
    </div>
  );
}

Exercici 3

Crea el component ResumFlota a src/components/ResumFlota.jsx. Ha de mostrar, dins d'una <section className="resum-flota">, un <h2> amb el text «Resum de la flota» i tres línies amb els totals de la flota de CicloUrbano segons el domini.js: total de bicicletes, disponibles i no disponibles. Escriu els números a mà (encara no sabem calcular-los a partir de les dades) i col·loca'l a App.jsx entre la capçalera i la llista. Després dibuixa l'arbre de components actualitzat.

Solucions

Solució 1.

Component Existeix? Tipus Criteri que ho justifica
DetallEstacio Nou Contenidor Responsabilitat única: coordina la pantalla; és la caixa on viurà la lògica de l'estació
TargetaEstacio Ja existeix (01-03) Presentació Reutilització: la capçalera amb nom, barri i places és la mateixa que al llistat d'estacions
DisponibilitatEstacio Nou Presentació Responsabilitat única: «20 places · 12 lliures» és una informació amb regles pròpies que també apareixerà al mapa
LlistaBicicletes Ja existeix Contenidor Reutilització: exactament el mateix bloc del catàleg, filtrat per estació
TargetaBicicleta Ja existeix Presentació Reutilització: és el mateix component sense cap canvi
EtiquetaEstat Ja existeix Presentació Reutilització: tercer ús del distintiu

El rellevant de l'exercici és que quatre dels sis components ja estaven escrits. Això és el que es guanya amb uns límits ben traçats: la segona pantalla costa molt menys que la primera.

Solució 2.

Problemes de disseny:

  1. Sense responsabilitat única. Tot fa de capçalera, navegació, catàleg, targeta i peu alhora; la seva descripció necessitaria quatre «i».
  2. Zero reutilització. El marcatge de la targeta està duplicat literalment, i el del distintiu d'estat també. Qualsevol canvi de disseny cal aplicar-lo dues vegades.
  3. Nom inútil. Tot no descriu cap responsabilitat, cosa que és el símptoma clàssic de límits mal traçats.
  4. Marcatge no semàntic. <div> per a les targetes i per a la navegació, en lloc de <article> i <nav>.

Descomposició proposada:

flowchart TD
    APP[App] --> CAB[Capcalera]
    APP --> LIS[LlistaBicicletes]
    APP --> PIE[PeuDePagina]
    CAB --> BIE[Benvinguda]
    LIS --> T1[TargetaBicicleta]
    LIS --> T2[TargetaBicicleta]
    T1 --> E1[EtiquetaEstat]
    T2 --> E2[EtiquetaEstat]
Component Responsabilitat
App Muntar les tres zones de la pantalla
Capcalera Identitat del lloc i navegació
Benvinguda Títol i eslògan
LlistaBicicletes Títol de la secció i conjunt de targetes
TargetaBicicleta Dades d'una bicicleta
EtiquetaEstat Distintiu visual de l'estat
PeuDePagina Informació de tancament

Solució 3.

// src/components/ResumFlota.jsx
function ResumFlota() {
  return (
    <section className="resum-flota">
      <h2>Resum de la flota</h2>
      <p>Total de bicicletes: 5</p>
      <p>Disponibles: 3</p>
      <p>No disponibles: 2</p>
    </section>
  );
}

export default ResumFlota;
// src/App.jsx
import Capcalera from './components/Capcalera.jsx';
import ResumFlota from './components/ResumFlota.jsx';
import LlistaBicicletes from './components/LlistaBicicletes.jsx';
import PeuDePagina from './components/PeuDePagina.jsx';

function App() {
  return (
    <>
      <Capcalera />
      <main>
        <ResumFlota />
        <LlistaBicicletes />
      </main>
      <PeuDePagina />
    </>
  );
}

export default App;

Arbre actualitzat:

flowchart TD
    APP[App] --> CAB[Capcalera]
    APP --> RES[ResumFlota]
    APP --> LIS[LlistaBicicletes]
    APP --> PIE[PeuDePagina]
    CAB --> BIE[Benvinguda]
    LIS --> T1[TargetaBicicleta]
    LIS --> T2[TargetaBicicleta]
    LIS --> T3[TargetaBicicleta]
    T1 --> E1[EtiquetaEstat]
    T2 --> E2[EtiquetaEstat]
    T3 --> E3[EtiquetaEstat]

Els números «5, 3, 2» surten de comptar a mà el domini.js, i aquest és precisament el problema: si demà entra una bicicleta nova, el resum mentirà. Derivar aquests totals de les dades és qüestió d'una lliçó.

Conclusió

Un component és la unitat de disseny de React: encapsula marcatge, lògica i estil d'un tros d'interfície, i el seu valor depèn sobretot d'on traces els seus límits. Ara tens un mètode per traçar-los: partir de l'esbós, envoltar les zones amb nom propi i validar cada candidat contra tres criteris —responsabilitat única, reutilització i mida manejable—, evitant tant el component que ho fa tot com la fragmentació en peces trivials.

Has convertit l'esbós del catàleg de CicloUrbano en un arbre de components real: App munta Capcalera, LlistaBicicletes i PeuDePagina; Capcalera conté Benvinguda; LlistaBicicletes conté TargetaBicicleta, i aquesta conté EtiquetaEstat. Saps distingir components de presentació de components contenidors i fer servir aquesta distinció per empènyer la lògica cap amunt i deixar les fulles reutilitzables. I tens fixades les convencions que seguirà tot el curs: un component per fitxer a src/components/, PascalCase, extensió .jsx, exportació per defecte per als components i amb nom per a les dades.

També has vist el sostre amb què xoquem: l'estructura és correcta però les tres targetes són idèntiques, perquè les dades continuen escrites dins dels components.

Abans de trencar aquest sostre, hi ha una parada obligatòria. Tot el que has escrit són components de funció, però en qualsevol projecte React amb alguns anys de vida trobaràs components escrits amb classes: una altra sintaxi, amb this, render() i constructor. No escriuràs codi nou així, però sí que necessites saber llegir-lo. Aquest és el tema de la lliçó següent: Components Funcionals vs de Classe.

Curs de React

Mòdul 1: Introducció a React

Mòdul 2: Components de React

Mòdul 3: Treballar amb Esdeveniments

Mòdul 4: Conceptes Avançats de Components

Mòdul 5: Hooks de React

Mòdul 6: Enrutament a React

Mòdul 7: Gestió de l'Estat

Mòdul 8: Optimització del Rendiment

Mòdul 9: Proves a React

Mòdul 10: Temes Avançats

Mòdul 11: Projecte: Construir una Aplicació Completa

© Copyright 2026. Tots els drets reservats