Skip to content

Component

Un component de React és una funció de TypeScript a la qual pots afegir {% link “/ts/react/tsx/” %}.

Crea un projecte amb {% link “/ts/bun/” %}

Terminal window
$ bun create vite react-component --template react-swc-ts
Scaffolding project in /home/box/react-component...
...

Defineix el component Greeting que és utilitzat pel component App:

export default function App() {
return <Greeting/>
}
function Greeting() {
return <p className="fs-3 p-4 border">Hello X</p>
}

Els components de React són funcions regulars de TypeScript, però els seus noms han de començar amb lletra majúscula o no funcionaran!

El component Greeting està nidat dins del component App.

Pots afegir tants components Greeting com vulguis dins del component App:

export default function App() {
return (
<>
<Greeting />
<Greeting />
<Greeting />
</>
)
}
function Greeting() {
return <p className="fs-3 m-2 p-2 border text-center">Hello X</p>
}

Al final el que el navegador veu és això:

<p class="fs-3 m-2 p-2 border text-center">Hello X</p>
<p class="fs-3 m-2 p-2 border text-center">Hello X</p>
<p class="fs-3 m-2 p-2 border text-center">Hello X</p>

Els components són funcions regulars de TypeScript, per això pots tenir múltiples components en el mateix arxiu.

Ja que els components Greeting es renderitzen dins d’App (fins i tot diverses vegades!) podem dir que App és un component pare, que renderitza cada Greeting com un «fill».

Aquesta és la part màgica de React: pots definir un component una vegada, i després utilitzar-lo en molts llocs i tantes vegades com vulguis.

{% panel ”⚠ Atención!” %}

Els components poden renderitzar altres components, però mai has de nidar les seves definicions:

export default function App() {
// 🔴 ¡Nunca definas un componente dentro de otro componente!
function Greeting() {
// ...
}
// ...
}

El fragment de codi de dalt és molt lent i causa errors.

En el seu lloc, defineix cada component en el primer nivell:

export default function App() {
// ...
}
// ✅ Declara los componentes en el primer nivel
function Greeting() {
// ...
}

{% endpanel %}

Els components de React utilitzen props per comunicar-se entre ells. Cada component pare pot enviar informació als seus components fills mitjançant l’ús de props.

Les props són les dades que es passen a un element TSX.

Per exemple, className, src i alt són algunes de les props que es poden passar a un element <img/>:

export default function App() {
return (
<>
<img
className="img-fluid"
src="https://shorturl.at/tQoUY"
alt="Rough Collie"
/>
</>
)
}

Les props que pots utilitzar amb un element <img/> estan predefinides (ReactDOM s’ajusta a l’estàndard HTML).

No obstant això, pots passar qualsevol prop als teus propis components per personalitzar-los.

En aquest codi, el component App no està passant cap prop al seu component fill, Greeting:

export default function App() {
return <Greeting/>
}
function Greeting() {
return <p className="fs-3 p-2"><span className="fs-1">🧔</span> Hello David!</p>
}

{% panel %}

🧔 Hello David!

{% endpanel %}

El més lògic seria que el component pare passés al component fill el nom i l’emoji.

Primer, passa les props name i emoji a l’element Greeting.

export default function App() {
return <Greeting name="Laura" emoji="👩‍🦰"/>
}

El component Greeting accepta aquestes props especificant els seus noms name i emoji separats per comes dins de ({ i }):

function Greeting({name, emoji}: { name: string, emoji: string }) {
return <p className="fs-3 p-2"><span className="fs-1">{emoji}</span> Hello {name}!</p>
}

Ara el component Greeting pot saludar correctament:

{% panel %}

👩‍🦰 Hello Laura!

{% endpanel %}

Les props compleixen el mateix paper que els arguments d’una funció — de fet, les props són l’únic argument del teu component!

Les funcions dels components de React accepten un únic argument, un objecte props:

function Greeting(props: { name: string, emoji: string }) {
return <p className="fs-3 p-2"><span className="fs-1">{props.emoji}</span> Hello {props.name}!</p>
}

En general, no necessites accedir a l’objecte complet de props, per això pots desestructurar-lo en props individuals:

function Greeting({name, emoji}: { name: string, emoji: string }) {
...
}

Aquesta sintaxi es coneix com a “desestructuració”.

Assignar un valor predeterminat per a una prop

Section titled “Assignar un valor predeterminat per a una prop”

Si vols assignar un valor predeterminat per a una prop en cas que no s’especifiqui cap valor, pots fer-ho mitjançant la desestructuració col·locant = seguit del valor predeterminat just després del paràmetre:

function Greeting({name, emoji = "👽"}: { name: string, emoji?: string }) {
return <p className="fs-3 p-2"><span className="fs-1">{emoji}</span> Hello {name}!</p>
}

Fixa’t que ara el tipus de props és { name: string, emoji?: string }, emoji és opcional.

Ara, si renderitzes Greeting sense la prop emoji, el valor d’emoji s’establirà automàticament a "👽".

export default function App() {
return <Greeting name="Armengol"/>
}

{% panel %}

👽 Hello Armengol!

{% endpanel %}

I ja tens un component completament reutilitzable:

export default function App() {
return (
<>
<Greeting name="Miquel" emoji="🧙‍♂️"/>
<Greeting name="Armengol"/>
<Greeting name="Nuria" emoji="🐱"/>
</>
)
}

{% panel %}

🧙‍♂️ Hello Miquel!

👽 Hello Armengol!

🐱 Hello Nuria!

{% endpanel %}

Una aplicació de React està formada per molts components nidats entre si.

React, com a framework d’UI, és independent de la plataforma, i una aplicació React es pot renderitzar a la web, la qual utilitza marcat HTML com les seves primitives, o podria renderitzar-se en una plataforma mòbil o d’escriptori, que podria utilitzar diferents primitives d’UI.

Igual que els navegadors i les plataformes mòbils, React utilitza estructures d’arbre per gestionar i modelar la relació entre els components en una aplicació de React.

Una característica important dels components és la capacitat de compondre components d’altres components. En nidar components, tenim el concepte de components pare i fill, on cada component pare pot ser alhora un fill d’un altre component.

App

Greeting

Greeting

Greeting

Quan renderitzem una aplicació de React, podem modelar aquesta relació en un arbre, conegut com l’arbre de renderització.

El node arrel en un arbre de renderització de React és el component arrel de l’aplicació. En aquest cas, el component arrel és App i és el primer component que React renderitza. Cada fletxa en l’arbre apunta des d’un component pare cap a un component fill.

  • L’arbre de renderització està compost únicament per components de React.
  • Un arbre de renderització representa una única passada de renderització d’una aplicació de React.
  • Amb renderització condicional, un component pare pot renderitzar diferents fills depenent de les dades passades.
  • L’arbre de renderització pot ser diferent per a cada passada de renderització.

React està dissenyat amb la suposició que cada component que escrius és una funció pura.

Això significa que un component sempre ha de retornar el mateix TSX donades les mateixes entrades, i no canviar qualsevol objecte o variable que existís abans de renderitzar.

A continuació tens un component que trenca aquesta regla:

let evil = "🤗"
export default function App() {
return (
<div className="container">
<Greeting name="Miquel" emoji="🧙‍♂️"/>
<Greeting name="Armengol"/>
<Greeting name="Nuria" emoji="🐱"/>
</div>
)
}
function Greeting({name, emoji = "👽"}: { name: string, emoji?: string }) {
evil = `${evil} ${emoji}`
return <p className="fs-4 p-2"><span className="fs-2">{evil}</span> Hello {name}!</p>
}

Pots veure que els emojis apareixen duplicats:

{% panel %}

🤗 🧙‍♂️ 🧙‍♂️ Hello Miquel!

🤗 🧙‍♂️ 🧙‍♂️ 👽 👽 Hello Armengol!

🤗 🧙‍♂️ 🧙‍♂️ 👽 👽 🐱 🐱 Hello Nuria!

{% endpanel %}

I més important, si canvies l’ordre dels Greeting, retornen un TSX diferent:

export default function App() {
return (
<div className="container">
<Greeting name="Nuria" emoji="🐱"/>
<Greeting name="Miquel" emoji="🧙‍♂️"/>
<Greeting name="Armengol"/>
</div>
)
}

{% panel %}

🤗 🐱 🐱 Hello Nuria!

🤗 🐱 🐱 🧙‍♂️ 🧙‍♂️ Hello Miquel!

🤗 🐱 🐱 🧙‍♂️ 🧙‍♂️ 👽 👽 Hello Armengol!

{% endpanel %}

La magia de los componentes reside en su reusabilidad: puedes crear componentes que se componen a su vez de otros componentes.

Pero mientras anidas más y más componentes, a menudo tiene sentido comenzar a separarlos en diferentes archivos.

Crea un fichero src/Greeting.tsx:

export default function Greeting({name, emoji = "👽"}: { name: string, emoji?: string }) {
return <p className="fs-4 p-2"><span className="fs-2">{emoji}</span> Hello {name}!</p>
}

Importa Greeting con un import por defecto desde Greeting.tsx:

import Greeting from './Greeting.tsx'
export default function App() {
return (
<div className="container">
<Greeting name="Nuria" emoji="🐱"/>
<Greeting name="Armengol"/>
</div>
)
}

Si quieres puedes canviar el nombre del componente cuando lo utilizas:

import Saluto from './Greeting.tsx'
export default function App() {
return (
<div className="container">
<Saluto name="Nuria" emoji="🐱"/>
<Saluto name="Armengol"/>
</div>
)
}

En lugar de utilizar un export por defecto puedes utilitzar un export por nombre (sin la palabra clave default):

export function Greeting({name, emoji = "👽"}: { name: string, emoji?: string }) {
return <p className="fs-4 p-2"><span className="fs-2">{emoji}</span> Hello {name}!</p>
}

Ahora tienes que importar el componente Greeting usando un import con nombre (con llaves):

import {Greeting} from './Greeting.tsx'
export default function App() {
return (
<div className="container">
<Greeting name="Nuria" emoji="🐱"/>
<Greeting name="Armengol"/>
</div>
)
}

¡Un archivo solo puede contener un export por defecto, pero puede tener múltiples exports con nombre!

Hay muchas librerías de react que puedes utilizar: Awesome React Components.

Pigeon Maps - Docs te proporciona un componente que te permite añadir mapas sin dependencias externas.

Instala la librería pigeon-maps:

Terminal window
bun add pigeon-maps

A continuación tenemos un mapa de Barcelona (Spain) con un marcador en la Sagrada Familia.

import {Map, Marker} from "pigeon-maps"
export default function App() {
return (
<div className="container p-5">
<Map height={600} defaultCenter={[41.40369, 2.17433]} defaultZoom={14}>
<Marker width={50} anchor={[41.40369, 2.17433]}/>
</Map>
</div>
)
}

{% panel %}

Pigeon | © OpenStreetMap contributors
{% endpanel %}

1.- DogCard

  • Crea un componente DogCard con las propiedades name,description e image?.
  • La propiedad image? tiene una imagen por defecto
  • Utiliza este componente para mostrar una colección de perros.

{% panel %}

Rough Collie
Rough Collie

A dog breed that's well-known for herding and protecting abilities, rough collie dogs are described as strong, loyal, affectionate, responsive, and fast.

Affenpinscher
Affenpinscher

Affenpinscher dogs are best known for their expressive face, with a short muzzle and dark, round eyes that give them an appearance not unlike a monkey.

{% endpanel %}

{% sol %}

export default function App() {
return (
<div className="container">
<div className="row p-2 justify-content-evenly">
<div className={"col-3"}>
<DogCard
name="Rough Collie"
description="A dog breed that's well-known for herding and protecting abilities, rough collie dogs are described as strong, loyal, affectionate, responsive, and fast."
image="https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/Rough-Collie-japan08_%28cropped%29.jpg/1200px-Rough-Collie-japan08_%28cropped%29.jpg"/>
</div>
<div className="col-3">
<DogCard name="Affenpinscher"
description="Affenpinscher dogs are best known for their expressive face, with a short muzzle and dark, round eyes that give them an appearance not unlike a monkey."/>
</div>
</div>
</div>
)
}
function DogCard({name, description, image = "https://d29fhpw069ctt2.cloudfront.net/icon/image/39024/preview.png"}: {
name: string,
description: string,
image?: string
}) {
return (
<div className="card">
<img className="card-img-top img-fluid"
src={image}
alt={name}/>
<div className="card-body">
<h5 className="card-title">{name}</h5>
<p className="card-text">{description}</p>
</div>
</div>
)
}

{% endsol %}

2.- Happy.tsx

  • Crea el fichero Happy.tsx con dos funciones con nombre: HappyName y HappyImage.
  • HappyName tiene una propiedad name y muestra el nombre de forma divertida.
  • HappyImage tiene una propiedad image y muestra la imagen de forma divertida.
  • El componente App tiene que utilizar los dos componentes de Happy.tsx

{% panel %}

Laura
{% endpanel %}

{% sol %} App.tsx

import {HappyName,HappyImage} from "./Happy.tsx";
export default function App() {
return (
<div className="container p-5">
<HappyName name="Laura"/>
<HappyImage image="https://upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Laura_Dern_Deauville_2017.jpg/440px-Laura_Dern_Deauville_2017.jpg"/>
</div>
)
}

Happy.tsx

export function HappyName({name}: { name: string }) {
return <span
className={"fs-3 border border-5 border-danger rounded-pill m-2 p-2 ps-5 pe-5 fw-semibold font-monospace"}>{name}</span>
}
export function HappyImage({image}: { image: string }) {
return <img src={image} className={"img-fluid img-thumbnail w-25 p-2 border border-5 border-primary"}/>
}

{% endsol %}

3.- Pigeon Maps

  • Busca en Internet las coordendas geográficas y un GeoJSON de París.
  • Crea un mapa de París y alrededores marcando los límites de París.

{% panel %}


El contingut d'aquest lloc web té llicència CC BY-NC-ND 4.0.

©2022-2025 xtec.dev