Saltar al contingut

Estat

Crea un nou projecte amb el nom react-state:

Terminal window
> bun create vite react-state --template react-swc-ts
> cd react-state
> ...

React et permet afegir controladors d’esdeveniments al teu TSX.

A continuació tens un botó que quan hi fas clic no fa res:

export default function App() {
return <button className="btn btn-primary m-5">No hago nada</button>
}

Un controlador d’esdeveniment és una funció que s’executarà quan l’usuari faci alguna cosa:

export default function App() {
// Controlador de evento
function handleClick() {
alert("Bienvendio al Norte!")
}
return <button className="btn btn-primary m-5">No hago nada</button>
}

Pots confirmar que el botó no fa res i la funció mai s’executa 🥳

El botó té la propietat “nullable” onClick?, el valor de la qual és una funció que s’executarà quan l’usuari faci clic al botó:

export default function App() {
// Controlador de evento
function handleClick() {
alert("Bienvendio al Norte!")
}
return <button className="btn btn-primary m-5" onClick={handleClick}>No hago nada</button>
}

Ara quan fas clic al botó, el navegador detecta aquest esdeveniment, i com que la propietat onClick del botó no és nul·la, crida a la funció corresponent.

Per conveni, és comú anomenar els controladors d’esdeveniments com handle seguit del nom de l’esdeveniment.

Sovint veuràs onClick={handleClick}, onMouseEnter={handleMouseEnter}, etcètera.

Afegeix un controlador per a onMouseEnter?:

export default function App() {
// Controlador de evento
function handleClick() {
alert("Bienvenido al Norte!")
}
function handleOnMouseEnter() {
alert("Entrando en territorio botón")
}
return <button
className="btn btn-primary m-5"
onClick={handleClick}
onMouseEnter={handleOnMouseEnter}
>
No hago nada
</button>
}

També pots passar directament una funció en lloc d’una referència a una funció:

export default function App() {
return <button
className="btn btn-primary m-5"
onClick={() => alert("Bienvenido al sur!")}
onMouseLeave={() => alert("Saliendo de territorio botón")}
>
Hago algo
</button>
}

A continuació tens un text editable:

export default function App() {
return <p
className="m-5 p-2 border"
contentEditable="true"
onKeyUp={() => alert("Por que tocas!")}
>
En un lugar de la Mancha, de cuyo nombre no quiero acordarme, no ha mucho tiempo que vivía un hidalgo de los de lanza en astillero, adarga antigua, rocín flaco y galgo corredor. Una olla de algo más vaca que carnero, salpicón las más noches, duelos y quebrantos los sábados, lantejas los viernes, algún palomino de añadidura los domingos, consumían las tres partes de su hacienda. El resto della concluían sayo de velarte, calzas de velludo para las fiestas, con sus pantuflos de lo mesmo, y los días de entresemana se honraba con su vellorí de lo más fino. Tenía en su casa una ama que pasaba de los cuarenta, y una sobrina que no llegaba a los veinte, y un mozo de campo y plaza, que así ensillaba el rocín como tomaba la podadera. Frisaba la edad de nuestro hidalgo con los cincuenta años; era de complexión recia, seco de carnes, enjuto de rostro, gran madrugador y amigo de la caza. Quieren decir que tenía el sobrenombre de Quijada, o Quesada, que en esto hay alguna diferencia en los autores que deste caso escriben; aunque por conjeturas verosímiles se deja entender que se llamaba Quijana. Pero esto importa poco a nuestro cuento: basta que en la narración dél no se salga un punto de la verdad.</p>
}

El text és editable perquè l’atribut contentEditable de l’element <p> és “true” (fes clic a dins i comença a escriure).

Afegeix algunes funcions més que responguin a esdeveniments diferents.

Si no saps quins, escriu on i deixa que l’editor et suggereixi les opcions disponibles.

Com els controladors d’esdeveniments són declarats dins d’un component, tenen accés a les props del component.

Crea un component MessageButton:

export default function App() {
return <div>
<MessageButton message="Hola" />
<MessageButton message="Adiós" />
</div>
}
function MessageButton({ message }: { message: string }) {
return <button className="btn btn-primary m-5" onClick={() => alert(message)}>
Hago algo
</button>
}

Ara tens dos botons que mostren missatges diferents, però que tenen el mateix text.

Modifica el codi per passar com a prop el contingut del botó:

export default function App() {
return (
<div>
<MessageButton message="Hola">Bienvenido al club</MessageButton>
<MessageButton message="Adiós">
<img src="https://www.flaticon.com/download/icon/1998592?icon_id=1998592&author=219&team=219&pack=1998559&style=Lineal+Color&style_id=914&format=png&color=%23000000&colored=2&size=64&selection=1&type=standard" />
</MessageButton>
</div>
)
}
function MessageButton({ message, children }: { message: string, children: any }) {
return <button className="btn btn-primary m-5" onClick={() => alert(message)}>
{children}
</button>
}

Pots veure que children és una prop especial de tipus any que es pot passar com si escrivissis HTML (és un objecte que conté els elements que embolcalla un component).

També pots passar com a prop el controlador d’esdeveniment:

export default function App() {
return (
<div>
<MessageButton onClick={() => alert("☀🌞🌊")}>
Playa
</MessageButton>
<MessageButton onClick={() => alert("☁⛅⛈🌤🌦🌧🌪🌫🌝")}>
Montaña
</MessageButton>
</div>
)
}
function MessageButton({ children, onClick }: { children: any, onClick: () => void }) {
return <button className="btn btn-primary m-5" onClick={onClick}>
{children}
</button>
}

Mostrar alertes no és la forma més eficient de mostrar missatges a l’usuari.

És millor utilitzar una variable:

export default function App() {
let message = "Hola Mundo"
return (
<div className="m-5">
<p className="ms-5">{message}</p>
<MessageButton onClick={() => message = "☀🌞🌊"}>
Playa
</MessageButton>
<MessageButton onClick={() => message = "☁⛅⛈🌤🌦🌧🌪🌫🌝"}>
Montaña
</MessageButton>
</div>
)
}
function MessageButton({ children, onClick }: { children: any, onClick: () => void }) {
return <button className="btn btn-primary m-5" onClick={onClick}>
{children}
</button>
}

No et preocupis si el missatge no canvia, el codi funciona, però React no reacciona al canvi del contingut de la variable message.

Comencem amb un exemple senzill:

export default function App() {
let message = "Hola Mundo"
return (
<div className="m-5">
<p>{message}</p>
<button className="btn btn-primary mt-3" onClick={() => message = "Ya sabes como funciona una variable de estado"}>
Mensaje
</button>
</div>
)
}

Com era d’esperar React no reacciona al canvi del contingut de la variable message.

React funciona d’aquesta manera:

  1. Renderitza l’aplicació

  2. Si es modifica una variable d’estat, React reacciona i torna a renderitzar els components afectats.

El Hook d’useState ofereix dues coses:

  1. Una variable d’estat per mantenir les dades entre renderitzats.
  2. Una funció que estableix l’estat per actualitzar la variable i provocar que React renderitzi el component novament.

Per afegir una variable d’estat, hem d’importar l’useState de React a l’inici de l’arxiu:

import { useState } from 'react';

I ja pots crear una variable amb estat:

import { useState } from "react"
export default function App() {
const [message, setMessage] = useState("Hola Mundo")
return (
<div className="m-5">
<p>{message}</p>
<button className="btn btn-primary mt-3"
onClick={() => setMessage("Ya sabes como funciona una variable de estado")}>
Mensaje
</button>
</div>
)
}

L’únic argument per a useState és el valor inicial de la seva variable d’estat.

En aquest exemple, el valor inicial de message s’estableix en "Hola Món".

Cada vegada que el component es renderitza, l’useState retorna un array que conté dos valors:

  1. La variable d’estat (message) amb el valor que has emmagatzemat.
  2. La funció que estableix l’estat (setMessage) que pot actualitzar la variable d’estat i alertar React perquè renderitzi el component novament.

Modifica aquest codi per utilitzar una variable d’estat:

export default function App() {
let message = "Hola Mundo"
return (
<div className="m-5">
<p className="ms-5">{message}</p>
<MessageButton onClick={() => message = "☀🌞🌊"}>
Playa
</MessageButton>
<MessageButton onClick={() => message = "☁⛅⛈🌤🌦🌧🌪🌫🌝"}>
Montaña
</MessageButton>
</div>
)
}
function MessageButton({ children, onClick }: { children: any, onClick: () => void }) {
return <button className="btn btn-primary m-5" onClick={onClick}>
{children}
</button>
}

Podem tenir més d’una variable d’estat de diferents tipus en un component.

A continuació tens un codi que utilitza 4 variables d’estat:

import { useState } from "react"
export default function App() {
const [message, setMessage] = useState("Adivina un número del 0 al 9")
const [number] = useState(Math.floor(Math.random() * 9))
const [attemps, setAttemps] = useState(4)
const [guessed, setGuessed] = useState(false)
function attempted(event: React.KeyboardEvent<HTMLInputElement>) {
const guess = Number(event.key)
if (isNaN(guess)) {
setMessage("Sólo numeros")
} else if (guess == number) {
setGuessed(true)
setMessage("Adivinado 🤡")
}
else {
setAttemps(attemps - 1)
setMessage(`Te quedan ${attemps} intentos`)
}
}
return (
<div className="m-5">
<p>{message}</p>
{guessed || attemps == 0 ? <p>El número es {number}</p> : <input onKeyUp={(event) => attempted(event)}></input>}
</div>
)
}

Activitat. Afegeix un botó per reiniciar el joc.

És una bona idea tenir múltiples variables d’estat si no es troben relacionades entre si, però si trobem que sovint canviem dues variables d’estat juntes, podria ser millor combinar-les en una sola.

TODO Exemple

L’estat és local per a una instància de component a la pantalla.

En altres paraules, si renderitzes el mateix component dues vegades, cada còpia tindrà un estat completament aïllat.

import { useState } from "react"
export default function App() {
return (
<div>
<Message text="☘ A la tercera va la vencida." />
<Message text="🌷 Al mal tiempo, buena cara." />
</div>
)
function Message({ text }: { text: string }) {
const [size, setSize] = useState(6)
function changeSize() {
if (size > 1)
setSize(size - 1)
}
return (
<div className="m-5">
<p className={`fs-${size}`}>{text}</p>
<button onClick={() => changeSize()}>
<span className="fs-1">+</span>
</button>
</div>
)
}
}

Pots veure en el nostre exemple, que el component Message es renderitza dues vegades i que són completament independents.

A continuació tens el codi inicial amb una llista de menjars en les quals només es mostra un menjar.

type Meal = {
name: string
ingredients: string[]
image: string
}
const meals: Meal[] = [
{
"name": "Classic Margherita Pizza",
"ingredients": ["Pizza dough", "Tomato sauce", "Fresh mozzarella cheese", "Fresh basil leaves", "Olive oil", "Salt and pepper to taste"],
"image": "https://cdn.dummyjson.com/recipe-images/1.webp",
},
{
"name": "Vegetarian Stir-Fry",
"ingredients": ["Tofu, cubed", "Broccoli florets", "Carrots, sliced", "Bell peppers, sliced", "Soy sauce", "Ginger, minced", "Garlic, minced", "Sesame oil", "Cooked rice for serving"],
"image": "https://cdn.dummyjson.com/recipe-images/2.webp",
},
{
"name": "Chocolate Chip Cookies",
"ingredients": ["All-purpose flour", "Butter, softened", "Brown sugar", "White sugar", "Eggs", "Vanilla extract", "Baking soda", "Salt", "Chocolate chips"],
"image": "https://cdn.dummyjson.com/recipe-images/3.webp",
},
{
"name": "Chicken Alfredo Pasta",
"ingredients": ["Fettuccine pasta", "Chicken breast, sliced", "Heavy cream", "Parmesan cheese, grated", "Garlic, minced", "Butter", "Salt and pepper to taste", "Fresh parsley for garnish"],
"image": "https://cdn.dummyjson.com/recipe-images/4.webp",
},
{
"name": "Mango Salsa Chicken",
"ingredients": ["Chicken thighs", "Mango, diced", "Red onion, finely chopped", "Cilantro, chopped", "Lime juice", "Jalapeño, minced", "Salt and pepper to taste", "Cooked rice for serving"],
"image": "https://cdn.dummyjson.com/recipe-images/5.webp",
}
]
export default function App() {
const meal = meals[0]
return (
<div className="container m-5">
<h5>{meal.name}</h5>
<div className="row">
<div className="col-3"><img className="img-fluid" src={meal.image} /></div>
</div>
</div>
)
}

Afegeix un botó per mostrar els ingredients que componen el menjar:

Afegeix dos botons: un per anar al següent plat i un altre per anar al plat anterior.

Crea una mini aplicació a la teva elecció amb router i estat.

Desplega l’aplicació a Netlify


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

©2022-2025 xtec.dev