Portes dues lliçons arrossegant la mateixa limitació: el catàleg de CicloUrbano mostra tres targetes idèntiques perquè les dades de la bicicleta estan escrites dins de TargetaBicicleta. Un component així no és reutilitzable: només serveix per mostrar aquella bicicleta concreta. Les props són la solució, i són un dels dos o tres conceptes veritablement fonamentals de React. Una prop és una dada que un component rep des de fora, de qui l'utilitza. Converteixen un component rígid en una plantilla parametritzable, defineixen el contracte entre pare i fill i són la raó per la qual pots escriure TargetaBicicleta una vegada i utilitzar-la en cinc pantalles diferents. En aquesta lliçó aprendràs a passar-les i rebre-les, quins tipus de valors admeten, com donar-los valors per defecte, què és la prop especial children, per què són de només lectura i com documentar el contracte dels teus components. En acabar, el catàleg mostrarà per fi bicicletes diferents.
Contingut
- Què són les props i quin problema resolen
- Passar props i rebre-les
- Destructuring a la signatura del component
- Quins tipus de valors es poden passar
- Valors per defecte
- La prop especial
children - Les props són de només lectura
- Difondre props amb l'operador de propagació
- Documentar el contracte d'un component
- El catàleg de CicloUrbano, ja parametritzat
- Què són les props i quin problema resolen
Props ve de properties. Són les dades que flueixen des d'un component pare cap a un component fill a través dels atributs del JSX.
L'analogia més útil és la d'una funció: un component és una funció, i les props són els seus arguments.
// Una funció sense paràmetres només sap fer una cosa
function saludar() {
return 'Hola, Ana Ribera';
}
// Una funció amb paràmetres serveix per a infinits casos
function saludar(nom) {
return `Hola, ${nom}`;
}Exactament el mateix passa amb els components:
// Component sense props: només sap mostrar una bicicleta
function TargetaBicicleta() {
return <h3>Urbana Clàssica</h3>;
}
// Component amb props: serveix per a qualsevol bicicleta
function TargetaBicicleta(props) {
return <h3>{props.model}</h3>;
}L'analogia no és casual: les props són literalment els arguments de la funció component. React recull els atributs que escrius al JSX, els posa dins d'un objecte i li passa aquest objecte a la teva funció com a primer paràmetre.
flowchart LR
A["App<br/>escriu el JSX"] -- "model='Urbana Clàssica'" --> B["React<br/>construeix { model: 'Urbana Clàssica' }"]
B -- "el passa com a argument" --> C["TargetaBicicleta(props)<br/>l'utilitza al seu JSX"]
I una propietat estructural molt important: el flux és unidireccional i descendeix per l'arbre. Un pare passa dades a un fill; un fill mai «retorna» dades escrivint sobre les seves props. Aquesta restricció, que d'entrada sembla limitant, és exactament el que fa predictible una aplicació React: per saber d'on surt una dada, puges per l'arbre; mai no has de buscar qui l'ha modificat per sorpresa.
- Passar props i rebre-les
Passar-les: atributs al JSX
Dues sintaxis conviuen, i la diferència és la que ja vas veure a la lliçó de JSX (01-04):
| Sintaxi | Quan s'utilitza | Exemple |
|---|---|---|
| Cometes | Només per a cadenes de text literals | model="Urbana Clàssica" |
| Claus | Per a qualsevol altra cosa: números, booleans, objectes, arrays, variables, expressions | preuHora={2.5} |
Un error clàssic: preuHora="2.5" passa la cadena "2.5", no el número. I "2.5".toFixed(2) no existeix, així que la targeta esclatarà. Si el valor no és text, va entre claus.
Rebre-les: el paràmetre props
// src/components/TargetaBicicleta.jsx
function TargetaBicicleta(props) {
return (
<article className="targeta-bicicleta">
<h3>{props.model}</h3>
<p>
<strong>{props.preuHora.toFixed(2)} € / hora</strong>
</p>
</article>
);
}
export default TargetaBicicleta;React crida la teva funció amb un únic objecte que conté tots els atributs que has escrit:
// El que React construeix internament i passa a la funció
{ model: 'Urbana Clàssica', preuHora: 2.5 }Tres detalls que convé fixar des del principi:
- El paràmetre es pot anomenar com vulguis.
propsés la convenció universal, però és un paràmetre normal. Anomena'lprops. - Els noms de les props els tries tu. No hi ha cap llista de props vàlides: són el vocabulari del teu component. Fes servir camelCase (
preuHora,estacioId), igual que a JavaScript. - Si no passes un atribut, aquesta prop val
undefined. No dona error; simplement no hi és. Per això més endavant veurem els valors per defecte.
- Destructuring a la signatura del component
Escriure props. davant de cada valor és sorollós. El destructuring d'ES6 permet extreure les props directament a la signatura de la funció, i és la forma dominant al React modern.
// Amb props.
function TargetaBicicleta(props) {
return (
<article className="targeta-bicicleta">
<h3>{props.model}</h3>
<p>Tipus: {props.tipus}</p>
<p>Estat: {props.estat}</p>
</article>
);
}// Amb destructuring a la signatura <- forma recomanada
function TargetaBicicleta({ model, tipus, estat }) {
return (
<article className="targeta-bicicleta">
<h3>{model}</h3>
<p>Tipus: {tipus}</p>
<p>Estat: {estat}</p>
</article>
);
}Les claus de ({ model, tipus, estat }) no són un objecte: són la sintaxi de desestructuració aplicada al paràmetre. És equivalent a escriure const { model, tipus, estat } = props; a la primera línia del cos.
Per què és millor:
| Avantatge | Explicació |
|---|---|
| Contracte visible | La signatura diu exactament què espera el component. Es llegeix com una documentació |
| Menys soroll | Desapareix el prefix props. de tot el JSX |
| Errors abans | Si escrius malament un nom a la signatura, ho detectes al lloc on importa |
| Valors per defecte senzills | S'escriuen allà mateix, com veuràs a l'apartat 5 |
Destructuring imbricat i parcial
A CicloUrbano passarem la bicicleta sencera com un objecte, no camp a camp. Es pot destructurar en dos nivells:
// Rebem la prop 'bicicleta' i la utilitzem com a objecte
function TargetaBicicleta({ bicicleta }) {
return <h3>{bicicleta.model}</h3>;
}
// O n'extraiem també els camps (destructuring imbricat)
function TargetaBicicleta({ bicicleta: { model, tipus, estat } }) {
return <h3>{model}</h3>;
}La segona forma és correcta, però perd la referència a l'objecte complet i es torna il·legible amb molts camps. Al curs farem servir la primera: rebem bicicleta i, si cal, la destructurem al cos.
- Quins tipus de valors es poden passar
Qualsevol valor de JavaScript. Literalment qualsevol.
| Tipus | Com es passa | Exemple a CicloUrbano |
|---|---|---|
| Cadena | Cometes o claus | model="Urbana Clàssica" |
| Número | Sempre claus | preuHora={2.5} |
| Booleà | Claus, o la drecera de l'atribut solt | destacada={true} o destacada |
| Objecte | Claus (dobles si és un literal) | bicicleta={bicicletes[0]} · bicicleta={{ id: 'bici-001' }} |
| Array | Claus | tipus={['urbana', 'electrica']} |
| Funció | Claus, sense parèntesis | alReservar={reservarBicicleta} |
| Element JSX | Claus | icona={<EtiquetaEstat estat="disponible" />} |
null / undefined |
Claus | estacio={null} |
Vegem els casos que més es presten a confusió.
Booleans i la drecera
<TargetaBicicleta bicicleta={bici} destacada={true} />
<TargetaBicicleta bicicleta={bici} destacada /> {/* Idèntic a l'anterior */}
<TargetaBicicleta bicicleta={bici} destacada={false} />
<TargetaBicicleta bicicleta={bici} /> {/* destacada és undefined, no false */}Escriure l'atribut solt equival a ={true}, igual que a HTML amb disabled o checked. Compte amb l'última línia: ometre la prop dona undefined, que és falsy però no és false. A la pràctica tots dos es comporten igual en una condició, però no són el mateix valor.
Objectes i la doble clau
{/* Passem una variable que ja és un objecte: unes claus */}
<TargetaBicicleta bicicleta={bicicletes[0]} />
{/* Escrivim l'objecte allà mateix: claus de l'expressió + claus de l'objecte */}
<TargetaBicicleta bicicleta={{ id: 'bici-001', model: 'Urbana Clàssica' }} />És la mateixa doble clau que ja vas veure a style={{ color: 'red' }}: les de fora obren una expressió JSX, les de dins són el literal d'objecte.
Funcions: sense parèntesis
{/* CORRECTE: passem la funció */}
<TargetaBicicleta bicicleta={bici} alReservar={reservarBicicleta} />
{/* INCORRECTE: l'executem ara i passem el seu resultat */}
<TargetaBicicleta bicicleta={bici} alReservar={reservarBicicleta()} />Passar funcions com a props és el mecanisme amb què un fill avisa el seu pare que alguna cosa ha passat. Aquí només cal que sàpigues que es pot fer i que la sintaxi és aquesta; el patró complet de comunicació fill → pare s'estudia a Elevar l'Estat, i els gestors d'esdeveniments a Gestió d'Esdeveniments.
Elements JSX com a props
Una prop pot contenir interfície. Això obre la porta a components molt flexibles:
- Valors per defecte
Si el pare no passa una prop, aquesta arriba com a undefined. Perquè el component continuï funcionant, se li donen valors per defecte amb els paràmetres per defecte de JavaScript, directament a la desestructuració:
// src/components/EtiquetaEstat.jsx
function EtiquetaEstat({ estat = 'disponible', compacta = false }) {
const classes = compacta
? `estat estat--${estat} estat--compacta`
: `estat estat--${estat}`;
return <span className={classes}>{estat}</span>;
}
export default EtiquetaEstat;<EtiquetaEstat estat="alquilada" /> {/* mostra 'alquilada', no compacta */}
<EtiquetaEstat /> {/* mostra 'disponible' per defecte */}
<EtiquetaEstat estat="carga" compacta />Un matís que causa sorpreses: el valor per defecte s'aplica només si la prop és undefined, no si és null, 0 o cadena buida.
<EtiquetaEstat estat={null} /> {/* estat val null, NO 'disponible' */}
<EtiquetaEstat estat="" /> {/* estat val '', NO 'disponible' */}Nota sobre defaultProps
En codi antic veuràs aquesta altra forma:
// FORMA ANTIGA — no la facis servir en components de funció
EtiquetaEstat.defaultProps = {
estat: 'disponible'
};defaultProps està eliminat per als components de funció a partir de React 19. Si el trobes, és codi anterior i cal traduir-lo a paràmetres per defecte. En components de classe continua funcionant, perquè una classe no té una signatura on escriure els valors per defecte.
- La prop especial
children
childrenHi ha una prop que no es passa com a atribut: tot el que escrius entre l'etiqueta d'obertura i la de tancament d'un component arriba a la prop children.
<Panell titol="Bicicletes disponibles">
<p>Això d'aquí dins és children</p>
<TargetaBicicleta bicicleta={bicicletes[0]} />
</Panell>El component que la rep:
// src/components/Panell.jsx
function Panell({ titol, children }) {
return (
<section className="panell">
<h2 className="panell__titol">{titol}</h2>
<div className="panell__cos">{children}</div>
</section>
);
}
export default Panell;children és la base del patró de composició: escrius components que aporten una estructura o un comportament i deixes que qui els utilitza decideixi el contingut.
// src/App.jsx (fragment)
<Panell titol="Bicicletes del centre">
<TargetaBicicleta bicicleta={bicicletes[0]} />
<TargetaBicicleta bicicleta={bicicletes[1]} />
</Panell>
<Panell titol="Avís">
<p>El servei de càrrega està en manteniment fins dilluns.</p>
</Panell>Un mateix Panell, dos continguts completament diferents. Sense children caldria inventar props per a cada cas possible, o duplicar el component.
Coses que convé saber sobre children:
- Pot ser qualsevol cosa: text, un element, diversos elements, un array, o res (
undefined). childrenno es pot passar també com a atribut. Si escrius<Panell children="hola">adéu</Panell>, el contingut entre etiquetes guanya.- Un component pot tenir diverses «zones» de contingut passant elements JSX per props amb nom (
capcalera={<…/>},peu={<…/>}). És el que de vegades s'anomena slots.
Aquest patró té molt més recorregut —contenidors genèrics, embolcalls, especialització de components— i s'aprofundeix a Composició vs Herència.
- Les props són de només lectura
Aquesta és la regla més important de la lliçó, i no admet excepcions:
Un component mai ha de modificar les seves pròpies props. React funciona sota la premissa que un component, davant les mateixes props, produeix sempre el mateix resultat. Això s'anomena ser una funció pura.
// PROHIBIT
function TargetaBicicleta({ bicicleta }) {
bicicleta.estat = 'alquilada'; // muta l'objecte del pare
return <h3>{bicicleta.model}</h3>;
}// PROHIBIT
function TargetaBicicleta(props) {
props.model = props.model.toUpperCase(); // assignació directa sobre props
return <h3>{props.model}</h3>;
}Què passa exactament si ho intentes
Depèn de què estiguis mutant, i cap dels dos escenaris és bo:
| Cas | Què passa |
|---|---|
props.algo = … |
React congela l'objecte props en desenvolupament: l'assignació falla en mode estricte amb TypeError: Cannot assign to read only property |
props.objecte.camp = … |
No hi ha error. Estàs mutant l'objecte original del pare, que React no ha congelat. Encara pitjor: React no s'assabenta del canvi, així que no es dispara cap render i la pantalla pot quedar desincronitzada de les dades |
El segon cas és el perillós: silenciós, difícil de depurar i capaç de produir «dades que canvien sense que ningú les pinti». A CicloUrbano, mutar bicicleta.estat dins de la targeta modificaria l'array bicicletes importat de domini.js per a tota l'aplicació.
Què fer en el seu lloc
- Si només necessites un valor transformat per pintar-lo, calcula una variable local. Això és perfectament correcte: el que està prohibit és escriure a la prop, no llegir-la ni derivar-ne.
function TargetaBicicleta({ bicicleta }) {
const modelEnMajuscules = bicicleta.model.toUpperCase(); // ✅ variable nova
const preuFormatat = bicicleta.preuHora.toFixed(2); // ✅
return (
<article className="targeta-bicicleta">
<h3>{modelEnMajuscules}</h3>
<p><strong>{preuFormatat} € / hora</strong></p>
</article>
);
}- Si la dada ha de canviar de veritat, no és una prop: és estat, i ho veuràs a la propera lliçó. I si el canvi l'origina el fill però la dada pertany al pare, el fill avisa mitjançant una funció rebuda per props (04-01).
- Difondre props amb l'operador de propagació
L'operador de propagació (...) permet passar totes les propietats d'un objecte com a props d'un cop:
const bici = { id: 'bici-001', model: 'Urbana Clàssica', tipus: 'urbana', estat: 'disponible', estacioId: 'est-01', preuHora: 2.5 };
// Escriure això...
<TargetaBicicleta {...bici} />
// ...equival exactament a això:
<TargetaBicicleta
id={bici.id}
model={bici.model}
tipus={bici.tipus}
estat={bici.estat}
estacioId={bici.estacioId}
preuHora={bici.preuHora}
/>I el component les rep com a props soltes:
També es pot combinar amb props explícites, i l'ordre mana: el que s'escriu després guanya.
<TargetaBicicleta {...bici} estat="mantenimiento" /> {/* estat = 'mantenimiento' */}
<TargetaBicicleta estat="mantenimiento" {...bici} /> {/* estat = 'disponible', l'spread trepitja */}Quan NO convé fer-lo servir
És una eina còmoda de la qual s'abusa amb facilitat. Els inconvenients:
| Problema | Explicació |
|---|---|
| El contracte desapareix | Llegint <TargetaBicicleta {...bici} /> no saps quines props rep realment el component. Cal obrir dos fitxers per esbrinar-ho |
| Props de més | S'hi colen totes les claus de l'objecte, incloses les que el component no fa servir (id, estacioId). Si algun dia es reenvien al DOM, apareixen avisos d'atributs desconeguts |
| Acoblament ocult | Si demà domini.js renomena preuHora a preu, el component deixa de rebre'l i no falla: simplement pinta undefined |
| Refactoritzacions a cegues | Buscar «qui passa la prop estat» no troba res, perquè ningú l'escriu |
Els usos legítims són els components embolcall que reenvien props que no coneixen:
// Un botó propi que afegeix estils i reenvia tota la resta al <button> real
function BotoMarca({ children, ...resta }) {
return (
<button className="boto-marca" {...resta}>
{children}
</button>
);
}
<BotoMarca type="submit" disabled>Reservar</BotoMarca>Aquí ...resta recull totes les props que no hem desestructurat (type, disabled, onClick…) i les reenvia. Aquest és el cas en què l'operador brilla.
Regla del curs: a CicloUrbano passarem l'objecte complet com una prop amb nom —bicicleta={bici}— en lloc de difondre'l. El contracte queda explícit i la signatura del component es llegeix sola.
- Documentar el contracte d'un component
Les props són el contracte públic del teu component. En JavaScript pur, res no impedeix passar un número on s'espera un objecte, així que convé deixar-ho escrit.
Nivell 1: un comentari a la capçalera
Costa trenta segons i resol el 80 % dels casos.
/**
* Targeta d'una bicicleta del catàleg de CicloUrbano.
*
* Props:
* - bicicleta (objecte, obligatori) { id, model, tipus, estat, estacioId, preuHora }
* - destacada (booleà, opcional, per defecte false) ressalta la targeta
*/
function TargetaBicicleta({ bicicleta, destacada = false }) { … }Nivell 2: propTypes
Abans que TypeScript es generalitzés, la comprovació de tipus a React es feia amb la llibreria prop-types, que valida en temps d'execució i només en desenvolupament, imprimint un avís a la consola:
import PropTypes from 'prop-types';
TargetaBicicleta.propTypes = {
bicicleta: PropTypes.shape({
model: PropTypes.string.isRequired,
preuHora: PropTypes.number.isRequired
}).isRequired,
destacada: PropTypes.bool
};Ho mencionem perquè el trobaràs en moltíssim codi existent, però avui està desaconsellat per a projectes nous: prop-types és una dependència extra, només avisa quan l'error ja ha passat i no ajuda l'editor.
Nivell 3: TypeScript
És la resposta moderna: els tipus es comproven abans d'executar, l'editor autocompleta les props i renombrar un camp es propaga sol.
El tipatge seriós de components és el contingut de TypeScript amb React. Fins llavors, a CicloUrbano farem servir el nivell 1: un comentari clar a sobre de cada component reutilitzable.
| Enfocament | Quan comprova | Cost | Recomanació |
|---|---|---|---|
| Comentari | Mai (el llegeix una persona) | Nul | ✅ Mínim imprescindible |
propTypes |
En execució, només en desenvolupament | Una dependència | Només en projectes que ja el fan servir |
| TypeScript | En escriure i en compilar | Configuració inicial | ✅ L'ideal en projectes seriosos |
- El catàleg de CicloUrbano, ja parametritzat
És el moment de trencar el sostre. Convertirem TargetaBicicleta i EtiquetaEstat en components parametritzats i pintarem bicicletes diferents.
EtiquetaEstat amb props
// src/components/EtiquetaEstat.jsx
/**
* Distintiu visual de l'estat d'una bicicleta.
* Props:
* - estat (cadena, opcional, per defecte 'disponible'):
* 'disponible' | 'alquilada' | 'mantenimiento'
*/
function EtiquetaEstat({ estat = 'disponible' }) {
return <span className={`estat estat--${estat}`}>{estat}</span>;
}
export default EtiquetaEstat;La classe es construeix amb una plantilla: estat--${estat} produeix estat--disponible, estat--alquilada o estat--mantenimiento, que són les classes CSS que vam definir a la lliçó 02-01. Tota la regla «quin color correspon a quin estat» viu ara en un sol lloc.
TargetaBicicleta amb props
// src/components/TargetaBicicleta.jsx
import EtiquetaEstat from './EtiquetaEstat.jsx';
/**
* Targeta d'una bicicleta del catàleg de CicloUrbano.
* Props:
* - bicicleta (objecte, obligatori) { id, model, tipus, estat, estacioId, preuHora }
* - nomEstacio (cadena, opcional, per defecte 'Estació desconeguda')
*/
function TargetaBicicleta({ bicicleta, nomEstacio = 'Estació desconeguda' }) {
const preuFormatat = bicicleta.preuHora.toFixed(2).replace('.', ',');
return (
<article className={`targeta-bicicleta targeta-bicicleta--${bicicleta.tipus}`}>
<h3>
{bicicleta.model} <EtiquetaEstat estat={bicicleta.estat} />
</h3>
<p>Tipus: {bicicleta.tipus}</p>
<p>Estació: {nomEstacio}</p>
<p>
<strong>{preuFormatat} € / hora</strong>
</p>
</article>
);
}
export default TargetaBicicleta;El que ha passat aquí, punt per punt:
{ bicicleta, nomEstacio = '…' }: destructuring a la signatura, amb un valor per defecte per a la prop opcional.preuFormatat: una variable derivada de la prop. Llegim la prop i calculem; no la modifiquem.toFixed(2)dona"2.50"i elreplaceel converteix al format decimal amb coma,"2,50".targeta-bicicleta--${bicicleta.tipus}: la classe de variant ja no està escrita a mà; surt de la dada.urbana,electricaocarga.<EtiquetaEstat estat={bicicleta.estat} />:TargetaBicicletaés pare d'EtiquetaEstati li passa una prop. Les props descendeixen per l'arbre de nivell en nivell.
LlistaBicicletes reparteix les dades
// src/components/LlistaBicicletes.jsx
import TargetaBicicleta from './TargetaBicicleta.jsx';
/**
* Secció del catàleg. Rep les tres primeres bicicletes per props.
* (A 03-03 passarà a rebre l'array complet i recórrer-lo amb map.)
*/
function LlistaBicicletes({ primera, segona, tercera }) {
return (
<section className="llista-bicicletes">
<h2>Bicicletes disponibles</h2>
<TargetaBicicleta bicicleta={primera} nomEstacio="Plaça Major" />
<TargetaBicicleta bicicleta={segona} nomEstacio="Plaça Major" />
<TargetaBicicleta bicicleta={tercera} nomEstacio="Parc Nord" />
</section>
);
}
export default LlistaBicicletes;Sí, tres props numerades és una solució lletja, i ho és a propòsit: és el símptoma que falta una eina. Aquesta eina és recórrer un array amb map, i arriba a Llistes i Claus. Fixa't que el problema ja no és de props, sinó de repetició.
App connecta les dades reals
// src/App.jsx
import { bicicletes } from './dades/domini.js';
import Capcalera from './components/Capcalera.jsx';
import LlistaBicicletes from './components/LlistaBicicletes.jsx';
import PeuDePagina from './components/PeuDePagina.jsx';
function App() {
return (
<>
<Capcalera />
<main>
<LlistaBicicletes
primera={bicicletes[0]}
segona={bicicletes[1]}
tercera={bicicletes[2]}
/>
</main>
<PeuDePagina />
</>
);
}
export default App;Desa i mira el navegador. Per fi:
| Targeta | Model | Distintiu | Vora de variant |
|---|---|---|---|
| 1 | Urbana Clàssica | verd disponible |
verd (urbana) |
| 2 | Elèctrica Pro | taronja alquilada |
taronja (electrica) |
| 3 | Càrrega Max | vermell mantenimiento |
vermell (carga) |
Tres targetes diferents produïdes per un únic component. Aquest és el moment en què React comença a rendibilitzar-se: qualsevol canvi de disseny a la targeta s'escriu una vegada i s'aplica a totes tres.
L'arbre de props
flowchart TD
DOM["domini.js<br/>bicicletes[]"] --> APP[App]
APP -- "primera / segona / tercera" --> LIS[LlistaBicicletes]
LIS -- "bicicleta, nomEstacio" --> T1["TargetaBicicleta<br/>bici-001"]
LIS -- "bicicleta, nomEstacio" --> T2["TargetaBicicleta<br/>bici-002"]
LIS -- "bicicleta, nomEstacio" --> T3["TargetaBicicleta<br/>bici-003"]
T1 -- "estat" --> E1[EtiquetaEstat]
T2 -- "estat" --> E2[EtiquetaEstat]
T3 -- "estat" --> E3[EtiquetaEstat]
Totes les fletxes apunten cap avall. Cap no puja. Això és el flux unidireccional de React.
Errors Comuns i Consells
- Passar números com a cadenes.
preuHora="2.5"entrega el text"2.5", i qualsevol operació aritmètica otoFixedfallarà. Si no és text literal, va entre claus. - Oblidar les claus de la desestructuració.
function TargetaBicicleta(bicicleta)rep l'objectepropscomplet sota el nombicicleta, així quebicicleta.modelésundefined. La signatura correcta ésfunction TargetaBicicleta({ bicicleta }). - Mutar una prop de tipus objecte.
bicicleta.estat = 'alquilada'no dona error i per això és tan perillós: modifiques la dada del pare sense que React ho sàpiga. Si la dada ha de canviar, és estat. - Executar la funció en passar-la.
alReservar={reservar()}cridareservardurant el render i passa el seu resultat. Sense parèntesis:alReservar={reservar}. - Confiar en el valor per defecte davant d'un
null. Només s'aplica ambundefined. Si el pare pot enviarnull, gestiona-ho explícitament. - Abusar de
{...objecte}. Còmode d'escriure, car de mantenir. Reserva'l per a embolcalls que reenvien props desconegudes. - Fer servir
defaultPropsen un component de funció. Eliminat a React 19. Fes servir paràmetres per defecte. - Consell: la signatura és documentació.
function TargetaBicicleta({ bicicleta, destacada = false })diu més que qualsevol comentari. Cuida els noms de les props: són el vocabulari públic del component. - Consell: si un component rep més de cinc o sis props, sospita. O fa massa coses, o diverses props haurien de viatjar agrupades en un objecte.
- Consell: inspecciona les props a DevTools. La pestanya Components mostra les props reals de cada instància. És la forma més ràpida de descobrir que estàs passant
undefined.
Exercicis
Exercici 1
Converteix TargetaEstacio (creada a la lliçó 01-03 amb dades fixes) en un component parametritzat. Ha de rebre una prop estacio amb la forma del domini.js i una prop opcional bicicletesEnEstacio (número, per defecte 0), i mostrar el nom, el barri, el total de places i el nombre de bicicletes. Després utilitza'l a App.jsx per pintar les tres estacions de CicloUrbano, escrivint-les una a una.
Exercici 2
Crea el component Panell descrit a l'apartat 6 (src/components/Panell.jsx), amb props titol (cadena, obligatòria) i children. Afegeix-hi una prop opcional peu que admeti un element JSX i es pinti sota el contingut si es proporciona. Després utilitza'l dues vegades a App.jsx: una embolicant la llista de bicicletes i una altra amb un avís de text.
Pista: per pintar el peu només si existeix, recorda que a JSX {undefined} no pinta res.
Exercici 3
Aquest component té quatre errors relacionats amb les props. Troba'ls, explica què provoca cadascun i escriu la versió corregida.
function FilaBicicleta(bicicleta, preuHora = 0) {
bicicleta.model = bicicleta.model.toUpperCase();
return (
<tr>
<td>{bicicleta.model}</td>
<td>{preuHora.toFixed(2)} €</td>
</tr>
);
}
// Ús:
<FilaBicicleta bicicleta={bicicletes[0]} preuHora="2.5" />Solucions
Solució 1.
// src/components/TargetaEstacio.jsx
/**
* Targeta d'una estació de CicloUrbano.
* Props:
* - estacio (objecte, obligatori) { id, nom, barri, places }
* - bicicletesEnEstacio (número, opcional, per defecte 0)
*/
function TargetaEstacio({ estacio, bicicletesEnEstacio = 0 }) {
const placesLliures = estacio.places - bicicletesEnEstacio;
return (
<article className="targeta-estacio">
<h3>{estacio.nom}</h3>
<p>Barri: {estacio.barri}</p>
<p>Places: {estacio.places}</p>
<p>
Bicicletes: {bicicletesEnEstacio} · Lliures: {placesLliures}
</p>
</article>
);
}
export default TargetaEstacio;// src/App.jsx (fragment)
import { estacions } from './dades/domini.js';
import TargetaEstacio from './components/TargetaEstacio.jsx';
<section>
<h2>Estacions</h2>
<TargetaEstacio estacio={estacions[0]} bicicletesEnEstacio={2} />
<TargetaEstacio estacio={estacions[1]} bicicletesEnEstacio={2} />
<TargetaEstacio estacio={estacions[2]} bicicletesEnEstacio={1} />
</section>Fixa't en placesLliures: és un valor derivat de les props, calculat a cada render. No és una prop ni cal desar-lo enlloc. Aquesta distinció entre el que es desa i el que es calcula serà central a la propera lliçó.
Solució 2.
// src/components/Panell.jsx
/**
* Contenidor amb títol i contingut lliure.
* Props:
* - titol (cadena, obligatori)
* - children (contingut, obligatori)
* - peu (element JSX, opcional)
*/
function Panell({ titol, children, peu }) {
return (
<section className="panell">
<h2 className="panell__titol">{titol}</h2>
<div className="panell__cos">{children}</div>
{peu && <div className="panell__peu">{peu}</div>}
</section>
);
}
export default Panell;// src/App.jsx (fragment)
<Panell titol="Catàleg" peu={<small>Preus amb IVA inclòs.</small>}>
<LlistaBicicletes
primera={bicicletes[0]}
segona={bicicletes[1]}
tercera={bicicletes[2]}
/>
</Panell>
<Panell titol="Avís">
<p>El servei de bicicletes de càrrega està en manteniment fins dilluns.</p>
</Panell>El mateix component embolica una llista completa en un cas i un paràgraf en l'altre: això és el que aporta children. L'expressió {peu && <div…>} és renderitzat condicional, que s'estudia a fons a Renderitzat Condicional; aquí n'hi ha prou de saber que si peu és undefined, no es pinta res.
Solució 3.
Els quatre errors:
- Signatura incorrecta.
function FilaBicicleta(bicicleta, preuHora = 0)tracta les props com dos paràmetres diferents. React sempre passa un únic objecte com a primer argument, així quebicicletaacaba sent l'objectepropssencer ipreuHorano rep mai res: es queda en el seu valor per defecte,0. Cal desestructurar:({ bicicleta, preuHora = 0 }). - Mutació d'una prop.
bicicleta.model = bicicleta.model.toUpperCase()modifica l'objecte deldomini.js, afectant tota l'aplicació i sense disparar cap render. Cal calcular una variable nova. - Número passat com a cadena.
preuHora="2.5"entrega el text"2.5";"2.5".toFixedno existeix i llançaTypeError. Ha de serpreuHora={2.5}. - Contracte incoherent.
preuHoraja ve dins de l'objectebicicleta; passar-lo a més per separat duplica la font de veritat i permet que les dues es contradiguin. S'ha de llegir de la bicicleta.
Versió corregida:
// src/components/FilaBicicleta.jsx
/**
* Fila d'una bicicleta per a vistes en taula.
* Props:
* - bicicleta (objecte, obligatori) { id, model, tipus, estat, estacioId, preuHora }
*/
function FilaBicicleta({ bicicleta }) {
const modelEnMajuscules = bicicleta.model.toUpperCase();
const preuFormatat = bicicleta.preuHora.toFixed(2).replace('.', ',');
return (
<tr>
<td>{modelEnMajuscules}</td>
<td>{preuFormatat} €</td>
</tr>
);
}
export default FilaBicicleta;Conclusió
Les props són el mecanisme amb què un component rep dades de l'exterior, i amb elles el catàleg de CicloUrbano ha deixat de ser una maqueta: una sola TargetaBicicleta pinta ara tres bicicletes diferents amb el seu model, el seu preu, la seva variant de color i el seu distintiu d'estat. Saps passar-les com a atributs —text entre cometes, tota la resta entre claus—, rebre-les amb destructuring a la signatura, donar-los valors per defecte amb paràmetres per defecte (i que defaultProps ja no existeix per a funcions a React 19), passar qualsevol tipus de valor —inclosos objectes, funcions i elements JSX— i utilitzar la prop especial children per construir contenidors com Panell que embolcallen contingut arbitrari.
I tens clara la regla que sosté tot el model: les props són de només lectura. Un component les llegeix i en deriva valors, però mai les escriu. El flux de dades baixa per l'arbre i no puja, cosa que fa que rastrejar l'origen de qualsevol dada sigui sempre possible. També saps que difondre props amb {...objecte} és còmode però esborra el contracte, i que aquest contracte convé documentar-lo, avui amb un comentari i en el futur amb TypeScript.
Queda una pregunta oberta, i és grossa: si les props vénen de fora i no es poden modificar, com canvia alguna cosa en una aplicació React? Quan l'usuari tria un filtre, reserva una bicicleta o prem un botó, alguna cosa ha de canviar i provocar un nou render. Aquesta cosa és l'estat: la memòria pròpia d'un component, la dada que sí que pot canviar i que, en fer-ho, dispara el cicle de renderitzat que vas estudiar a 01-05. És el tema de la propera lliçó: Estat: Gestió de l'Estat del Component.
Curs de React
Mòdul 1: Introducció a React
- Què és React?
- Configuració de l'Entorn de Desenvolupament
- Hola Món amb React
- JSX: Extensió de Sintaxi de JavaScript
- Com Renderitza React: Virtual DOM i Reconciliació
Mòdul 2: Components de React
- Entendre els Components
- Components Funcionals vs de Classe
- Props: Passar Dades als Components
- State: Gestió de l'Estat del Component
- Estils en els Components: CSS, Mòduls i Utilitats
Mòdul 3: Treballar amb Esdeveniments
- Gestió d'Esdeveniments a React
- Renderitzat Condicional
- Llistes i Claus
- Formularis i Components Controlats
- Validació de Formularis i Components No Controlats
- Accessibilitat en Components Interactius
Mòdul 4: Conceptes Avançats de Components
- Elevar l'Estat
- Composició vs Herència
- Mètodes del Cicle de Vida de React
- Hooks: Introducció i Ús Bàsic
- Límits d'Error: Capturar Fallades a la Interfície
Mòdul 5: Hooks de React
- Hook useState
- Hook useEffect
- Hook useRef i Accés al DOM
- Hook useContext
- Hook useReducer
- Hooks Personalitzats
Mòdul 6: Enrutament a React
- Introducció a React Router
- Configuració de React Router
- Rutes Imbricades
- Navegació Programàtica
- Rutes Protegides i Control d'Accés
Mòdul 7: Gestió de l'Estat
- Introducció a la Gestió de l'Estat
- API de Context
- Redux: Introducció i Configuració
- Redux: Accions i Reductors
- Redux: Connectar-lo a React
- Estat del Servidor: Peticions, Memòria Cau i Sincronització
Mòdul 8: Optimització del Rendiment
- Tècniques d'Optimització del Rendiment a React
- Memoïtzació amb React.memo
- Hooks useMemo i useCallback
- Divisió de Codi i Càrrega Mandrosa
- Mesurar el Rendiment amb React DevTools Profiler
Mòdul 9: Proves a React
- Introducció a les Proves
- Proves Unitàries amb Jest
- Proves de Components amb React Testing Library
- Proves de Codi Asíncron i Simulació d'APIs
- Proves d'Extrem a Extrem amb Cypress
Mòdul 10: Temes Avançats
- Renderitzat al Servidor (SSR) amb Next.js
- Generació de Llocs Estàtics (SSG) amb Next.js
- Suspense i React Server Components
- TypeScript amb React
- React Native: Creació d'Aplicacions Mòbils
