Aggiungere le Interazioni
Alcune cose sullo schermo si aggiornano in risposta all’input dell’utente. Ad esempio, cliccare su una galleria d’immagini modifica l’immagine corrente. In React, i dati che cambiano nel tempo vengono chiamati state. Puoi aggiungere lo state a qualsiasi componente e aggiornarlo secondo necessità. In questo capitolo, imparerai a scrivere componenti che gestiscono interazioni, aggiornano il loro state, e visualizzano diversi output nel tempo.
In questo capitolo
- Come gestire gli eventi originati dall’utente
- Come far “ricordare” le informazioni ai componenti tramite lo state
- Come React aggiorna la UI in due fasi
- Perché lo state non si aggiorna subito dopo averlo modificato
- Come accodare più aggiornamenti dello state
- Come aggiornare un oggetto nello state
- Come aggiornare un array nello state
Rispondere agli eventi
React ti consente di aggiungere degli event handler al tuo JSX. Gli event handler sono le tue funzioni personalizzate che vengono chiamate in risposta alle interazioni dell’utente, come il click, il passaggio del mouse, la messa a fuoco degli input del form e così via.
Componenti integrati come <button>
supportano solo eventi integrati del browser come onClick
. Tuttavia, è possibile anche creare i propri componenti e dare ai loro event handler dei nomi specifici, contestualizzati all’applicazione.
export default function App() { return ( <Toolbar onPlayMovie={() => alert('Playing!')} onUploadImage={() => alert('Uploading!')} /> ); } function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> Play Movie </Button> <Button onClick={onUploadImage}> Upload Image </Button> </div> ); } function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); }
Ready to learn this topic?
Leggi Rispondere agli Eventi per imparare ad aggiungere gli event handler.
Read MoreState: La memoria di un componente
Spesso i componenti devono cambiare ciò che c’è sullo schermo in seguito a un’interazione. Scrivere nel form deve aggiornare l’input, cliccare “avanti” su un carosello deve cambiare l’immagine mostrata, cliccare “acquista” inserisce un prodotto nel carrello. I componenti devono “ricordare” le cose: l’attuale valore dell’input, l’attuale immagine, il carrello. In React, questo specifico tipo di memoria è detto state.
Puoi aggiungere lo state a un componente con un hook useState
. Gli Hook sono speciali funzioni che consentono ai componenti di usare le funzionalità di React (lo state è una di queste). L’hook useState
consente di dichiarare una variabile state. Prende lo state iniziale e restituisce una coppia di valori: lo state attuale e una funzione state setter che ti consente di aggiornarlo.
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
Ecco come una galleria di immagini usa e aggiorna lo state al click:
import { useState } from 'react'; import { sculptureList } from './data.js'; export default function Gallery() { const [index, setIndex] = useState(0); const [showMore, setShowMore] = useState(false); const hasNext = index < sculptureList.length - 1; function handleNextClick() { if (hasNext) { setIndex(index + 1); } else { setIndex(0); } } function handleMoreClick() { setShowMore(!showMore); } let sculpture = sculptureList[index]; return ( <> <button onClick={handleNextClick}> Next </button> <h2> <i>{sculpture.name} </i> by {sculpture.artist} </h2> <h3> ({index + 1} of {sculptureList.length}) </h3> <button onClick={handleMoreClick}> {showMore ? 'Hide' : 'Show'} details </button> {showMore && <p>{sculpture.description}</p>} <img src={sculpture.url} alt={sculpture.alt} /> </> ); }
Ready to learn this topic?
Leggi State: La Memoria di un Componente per imparare a memorizzare un valore e aggiornarlo in base alle interazioni.
Read MoreRenderizzazione e commit
Prima che i tuoi componenti siano visualizzati sullo schermo, devono essere renderizzati da React. Comprendere le fasi di questo processo ti aiuterà a riflettere su come viene eseguito il tuo codice e a spiegarne il comportamento.
Immagina che i tuoi componenti siano cuochi in cucina, che assemblano deliziosi piatti. In questo scenario, React è il cameriere che prende le comande dai clienti e porta loro gli ordini. Questo processo di richiesta e servizio della UI ha tre fasi:
- Triggerare un render (portare la comanda in cucina)
- Renderizzare il componente (preparare l’ordine in cucina)
- Committare i cambiamenti al DOM (portare l’ordine al tavolo)
Illustrato da Rachel Lee Nabors
Ready to learn this topic?
Leggi Renderizzazione e Commit per imparare il ciclo di vita di un aggiornamento UI.
Read MoreLo State come istantanea
A differenza delle normali variabili JavaScript, lo state di React si comporta più come un’istantanea. Impostarlo non modifica la variabile state che già possiedi, ma triggera un re-render. Questo può sorprendere all’inizio!
console.log(count); // 0
setCount(count + 1); // Richiedi un re-render con 1
console.log(count); // Ancora 0!
Questo comportamento aiuta a evitare bug difficili da individuare. Ecco una piccola app di messaggistica. Prova a indovinare cosa succede se premi “Send” e poi selezioni il destinatario Bob. Quale nome apparirà nell’alert
cinque secondi dopo?
import { useState } from 'react'; export default function Form() { const [to, setTo] = useState('Alice'); const [message, setMessage] = useState('Hello'); function handleSubmit(e) { e.preventDefault(); setTimeout(() => { alert(`You said ${message} to ${to}`); }, 5000); } return ( <form onSubmit={handleSubmit}> <label> To:{' '} <select value={to} onChange={e => setTo(e.target.value)}> <option value="Alice">Alice</option> <option value="Bob">Bob</option> </select> </label> <textarea placeholder="Message" value={message} onChange={e => setMessage(e.target.value)} /> <button type="submit">Send</button> </form> ); }
Ready to learn this topic?
Leggi Lo State come Istantanea per imparare perché lo state appare “fisso” e inalterabile dentro gli event handler.
Read MoreAccodare più aggiornamenti dello state
Questo componente è difettoso: cliccare “+3” incrementa lo score soltanto una volta.
import { useState } from 'react'; export default function Counter() { const [score, setScore] = useState(0); function increment() { setScore(score + 1); } return ( <> <button onClick={() => increment()}>+1</button> <button onClick={() => { increment(); increment(); increment(); }}>+3</button> <h1>Score: {score}</h1> </> ) }
Lo State come Istantanea spiega a cosa è dovuto questo. Impostare lo state richiede un nuovo re-render, ma non lo modifica nel codice già eseguito. Quindi score
continua a essere 0
subito dopo aver chiamato setScore(score + 1)
.
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
Puoi risolvere questo problema passando una funzione updater quando imposti lo state. Nota come sostituire setScore(score + 1)
con setScore(s => s + 1)
aggiusta il pulsante “+3”. Questo consente di accodare più aggiornamenti dello state.
import { useState } from 'react'; export default function Counter() { const [score, setScore] = useState(0); function increment() { setScore(s => s + 1); } return ( <> <button onClick={() => increment()}>+1</button> <button onClick={() => { increment(); increment(); increment(); }}>+3</button> <h1>Score: {score}</h1> </> ) }
Ready to learn this topic?
Leggi Accodare più Aggiornamenti dello State per imparare ad accodare una sequenza di aggiornamenti dello state.
Read MoreAggiornare gli oggetti nello state
Lo State può contenere qualsiasi tipo di valore JavaScript, inclusi gli oggetti. Tuttavia, non dovresti modificare direttamente gli oggetti e gli array contenuti nello State. Quando vuoi aggiornare un oggetto o un array, dovresti invece crearne uno nuovo (o copiare quello esistente) e, infine, impostare la copia nello state.
Solitamente, userai la sintassi di spread ...
per copiare gli oggetti e gli array che desideri modificare. Ad esempio, l’aggiornamento di un oggetto nidificato potrebbe apparire così:
import { useState } from 'react'; export default function Form() { const [person, setPerson] = useState({ name: 'Niki de Saint Phalle', artwork: { title: 'Blue Nana', city: 'Hamburg', image: 'https://i.imgur.com/Sd1AgUOm.jpg', } }); function handleNameChange(e) { setPerson({ ...person, name: e.target.value }); } function handleTitleChange(e) { setPerson({ ...person, artwork: { ...person.artwork, title: e.target.value } }); } function handleCityChange(e) { setPerson({ ...person, artwork: { ...person.artwork, city: e.target.value } }); } function handleImageChange(e) { setPerson({ ...person, artwork: { ...person.artwork, image: e.target.value } }); } return ( <> <label> Name: <input value={person.name} onChange={handleNameChange} /> </label> <label> Title: <input value={person.artwork.title} onChange={handleTitleChange} /> </label> <label> City: <input value={person.artwork.city} onChange={handleCityChange} /> </label> <label> Image: <input value={person.artwork.image} onChange={handleImageChange} /> </label> <p> <i>{person.artwork.title}</i> {' by '} {person.name} <br /> (located in {person.artwork.city}) </p> <img src={person.artwork.image} alt={person.artwork.title} /> </> ); }
Se copiare gli oggetti diventa tedioso, puoi usare una libreria come Immer per ridurre il codice ripetitivo:
import { useImmer } from 'use-immer'; export default function Form() { const [person, updatePerson] = useImmer({ name: 'Niki de Saint Phalle', artwork: { title: 'Blue Nana', city: 'Hamburg', image: 'https://i.imgur.com/Sd1AgUOm.jpg', } }); function handleNameChange(e) { updatePerson(draft => { draft.name = e.target.value; }); } function handleTitleChange(e) { updatePerson(draft => { draft.artwork.title = e.target.value; }); } function handleCityChange(e) { updatePerson(draft => { draft.artwork.city = e.target.value; }); } function handleImageChange(e) { updatePerson(draft => { draft.artwork.image = e.target.value; }); } return ( <> <label> Name: <input value={person.name} onChange={handleNameChange} /> </label> <label> Title: <input value={person.artwork.title} onChange={handleTitleChange} /> </label> <label> City: <input value={person.artwork.city} onChange={handleCityChange} /> </label> <label> Image: <input value={person.artwork.image} onChange={handleImageChange} /> </label> <p> <i>{person.artwork.title}</i> {' by '} {person.name} <br /> (located in {person.artwork.city}) </p> <img src={person.artwork.image} alt={person.artwork.title} /> </> ); }
Ready to learn this topic?
Leggi Aggiornare gli Oggetti nello State per imparare ad aggiornare gli oggetti correttamente.
Read MoreAggiornare gli array nello state
Gli array sono un altro tipo di oggetti JavaScript mutabili che puoi memorizzare nello state e che dovresti trattare come read-only. Come per gli oggetti, quando vuoi aggiornare un array contenuto nello state, devi crearne uno nuovo (o copiare l’esistente), e infine impostare il nuovo array nello state.
import { useState } from 'react'; let nextId = 3; const initialList = [ { id: 0, title: 'Big Bellies', seen: false }, { id: 1, title: 'Lunar Landscape', seen: false }, { id: 2, title: 'Terracotta Army', seen: true }, ]; export default function BucketList() { const [list, setList] = useState( initialList ); function handleToggle(artworkId, nextSeen) { setList(list.map(artwork => { if (artwork.id === artworkId) { return { ...artwork, seen: nextSeen }; } else { return artwork; } })); } return ( <> <h1>Art Bucket List</h1> <h2>My list of art to see:</h2> <ItemList artworks={list} onToggle={handleToggle} /> </> ); } function ItemList({ artworks, onToggle }) { return ( <ul> {artworks.map(artwork => ( <li key={artwork.id}> <label> <input type="checkbox" checked={artwork.seen} onChange={e => { onToggle( artwork.id, e.target.checked ); }} /> {artwork.title} </label> </li> ))} </ul> ); }
Se copiare gli array diventa tedioso, puoi usare una libreria come Immer per ridurre il codice ripetitivo:
import { useState } from 'react'; import { useImmer } from 'use-immer'; let nextId = 3; const initialList = [ { id: 0, title: 'Big Bellies', seen: false }, { id: 1, title: 'Lunar Landscape', seen: false }, { id: 2, title: 'Terracotta Army', seen: true }, ]; export default function BucketList() { const [list, updateList] = useImmer(initialList); function handleToggle(artworkId, nextSeen) { updateList(draft => { const artwork = draft.find(a => a.id === artworkId ); artwork.seen = nextSeen; }); } return ( <> <h1>Art Bucket List</h1> <h2>My list of art to see:</h2> <ItemList artworks={list} onToggle={handleToggle} /> </> ); } function ItemList({ artworks, onToggle }) { return ( <ul> {artworks.map(artwork => ( <li key={artwork.id}> <label> <input type="checkbox" checked={artwork.seen} onChange={e => { onToggle( artwork.id, e.target.checked ); }} /> {artwork.title} </label> </li> ))} </ul> ); }
Ready to learn this topic?
Leggi Aggiornare gli Array nello State per imparare ad aggiornare gli array correttamente.
Read MoreQual è il prossimo passo?
Dirigiti su Rispondere agli Eventi per iniziare a leggere questo capitolo pagina per pagina!
Oppure, se hai già familiarità con questi argomenti, perché non leggere Gestire lo State?