Abbiamo ampiamente parlato dellโoggetto props che possiamo usare per passare diverse informazioni ai nostri componenti. In questo articolo introdurremo un secondo concetto fondamentale: lโoggetto state. Finora, infatti, non abbiamo potuto apprezzare in pieno le potenzialitร di React e ci siamo limitati a vedere componenti configurabili ma statici. A partire da questo articolo, renderemo i nostri componenti dinamici e vedremo come sia possibile eseguire diverse azioni in seguito allโinterazione dellโutente con il componente.
Class Component e oggetto State
Ancora una volta, React rende il tutto estremamente semplice. Ricordiamo che lโunica responsabilitร dellโoggetto props, che รจ un oggetto di sola lettura, รจ quella di passare le informazioni da un componente ai componenti figli. A differenza dellโoggetto props, costituito da valori statici, lโoggetto state rappresenta un insieme di valori che รจ gestito e aggiornato dal componente in cui รจ incapsulato. Se per lโoggetto props, lโunica regola da ricordare era di non modificarne mai il suo valore o quello delle sue proprietร , per lโoggetto state dobbiamo tenere in mente che possiamo modificarlo solo attraverso lโuso della funzione setState(), ereditata da tutti i Class Component. Unโimportante differenza, infatti, tra questโultimi e i Functional Component รจ che solo i primi possono avere uno stato interno. I Functional Component, al contrario, sono stateless perciรฒ non potranno mai avere uno stato interno.
// Non aggiornare mai 'state' direttamente, React non aggiornerร il componente
this.state.counter = 1 // NO!
// Versione Corretta
this.setState({counter: 1}); // OK
Ogni volta che chiameremo la funzione setState(), React aggiornerร lโoggetto state con le nuove informazioni. Se poi passeremo le proprietร dellโoggetto state ai componenti figli tramite props, alla ricezione del nuovo valore, React aggiornerร anche tali componenti. Approfondiremo lโargomento quando parleremo del ciclo di vita dei Componenti.
Lโoggetto state รจ dunque incapsulato allโinterno di un componente che รจ lโunico ad avere il diritto e la responsabilitร di modificarlo. Ogni componente non saprร mai se gli altri presentano uno stato interno o meno.
Nellโesempio sottostante vedremo come usare la funzione setState() per aggiornare un componente.
class Dado extends React.Component {
constructor(props) {
super(props);
this.state = {numeroEstratto: 0};
}
randomNumber() {
return Math.round(Math.random() * 5) + 1;
}
lanciaDado() {
this.setState({numeroEstratto: this.randomNumber()});
}
render() {
let valore;
if (this.state.numeroEstratto === 0) {
valore = <small>Lancia il dado cliccando <br /> sul pulsante sottostante</small>;
}else{
valore = <span>{this.state.numeroEstratto}</span>;
}
return (
<div className="card" >
<p className="card__number">{valore}</p>
<button className="card__button" onClick={() => this.lanciaDado()} >Lancia il Dado</button>
</div>
)
}
}
Per prima cosa creiamo un componente Dado. Nel costruttore usiamo il metodo super(props) per invocare il costruttore dellโoggetto padre e inizializzare correttamente il componente. Se non invochiamo il metodo super(props), non possiamo usare la keyword this allโinterno del costruttore. Definiamo anche un oggetto state con una sola proprietร che useremo per tener traccia del numero estratto ad ogni lancio. Nella funzione render() mostriamo un messaggio iniziale se non รจ mai stato premuto il pulsante โLancia il dadoโ; in caso contrario, verrร mostrato il numero estratto ad ogni lancio. Ogni volta che viene premuto il pulsante, viene invocata la funzione lanciaDado() che calcola un numero casuale compreso fra 1 e 6 e lo assegna a this.state.numeroEstratto tramite la funzione setState(). Trascuriamo per ora alcuni importanti dettagli in merito allโuso della keyword this allโinterno dei componenti. Tratteremo lโargomento in maniera approfondita nei prossimi articoli.
Proprietร della funzione setState()
La funzione setState() presenta alcune caratteristiche che รจ bene tenere in considerazione durante lo sviluppo delle nostre applicazioni.
React potrebbe raggruppare piรน chiamate della funzione setState() ed eseguire lโaggiornamento dellโoggetto state una volta sola. Lโaggiornamento degli oggetti state e props puรฒ avvenire in maniera asincrona e per questo motivo, per calcolare lo stato futuro di un componente, usando i valori attuali dellโoggetto state e props, dovremmo usare unโaltra versione di setState() che riceve come unico argomento una funzione. A questa vengono passati due argomenti, ovvero i valori correnti degli oggetti state e props.
/* Facciamo riferimento all'esempio presente nella documentazione ufficiale
*
* Supponiamo che al momento di invocare this.setState,
* un componente presenti il seguente stato:
*
* this.state = {counter: 1}
*
* Quando verrร chiamata la funzione setState, prevState.counter
* sarร uguale a 1
*/
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
Inoltre se un componente presenta un oggetto state con piรน coppie chiave-valore, volendo aggiornare una sola di queste, basterร passare alla funzione setState() un oggetto con i soli valori che si intendono modificare. React provvederร a combinare il nuovo oggetto passato con i valori attuali dellโoggetto state
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
proprieta1: "valore1",
proprieta2: "valore2",
proprieta3: [1,2,3,4,5]
}
}
// ... resto del componente
}
// ...
// ...
// Se a un certo punto invochiamo
this.setState({proprieta1: "nuovoValore1"})
// Dopo aver chiamato la funzione setState(), avremo il seguente oggetto state
this.state = {
proprieta1: "nuovoValore1", // verrร aggiornata solo proprieta1
proprieta2: "valore2",
proprieta3: [1,2,3,4,5]
}
Flusso unidirezionale di dati
Al contrario di altri framework, React introduce il concetto di flusso di dati unidirezionale (Unidirectional Data Flow). Non tutti i componenti sono uguali allโinterno della nostra applicazione. Non tutti i componenti dovranno avere uno stato interno, al contrario รจ consigliato costruire componenti senza stato (stateless). Possiamo vedere la nostra applicazione come una gerarchia di componenti. Solitamente avremo qualche componente ai vertici che sarร responsabile di mantenere lo stato della nostra applicazione e passerร le informazioni giรน ai componenti figli tramite props. Possiamo immaginare il flusso di informazioni come una cascata.
Definiamo dunque un oggetto, contentente le informazioni dinamiche della nostra applicazione. Non faranno parte di tale oggetto le informazioni dinamiche derivabili o calcolabili a partire da altre. Questo oggetto costituisce lo stato dellโintera applicazione. Fatto ciรฒ, dovremo affidare a un componente il compito di mantenere e modificare tale oggetto. Le informazioni mantenute in esso verranno poi passate ai componenti figli mediante lโuso dellโoggetto props.
Vediamo rapidamente un esempio pratico per capire meglio quanto appena descritto. Supponiamo di avere unโapplicazione Rubrica con una serie di contatti. ร possibile cercare tra i contatti tramite un campo di ricerca. Ogni volta che digiteremo un carattere nel campo di ricerca, verrร filtrata la lista dei contatti in base al testo digitato.
Allโinterno della nostra applicazione, lโunica informazione dinamica, che cambia in seguito allโinterazione dellโutente e non puรฒ essere derivata da niente altro, รจ il testo che digitiamo allโinterno del campo di ricerca. La lista dei contatti sarร invece passata attraverso lโoggetto props alla nostra applicazione. In unโapplicazione reale potrebbe capitare di dovere scaricare da un server la lista aggiornata dei contatti, ma anche in questo caso sarร passata al componente piรน esterno App attraverso lโoggetto props. I due componenti che necessiteranno di usare il testo digitato saranno SearchBar e ContactList. Questโultimo lo utilizzerร per filtrare e mostrare una lista aggiornata dei contatti.
Lโoggetto state iniziale della nostra applicazione sarร quindi {campoDiRicerca: โ}. Dobbiamo determinare qual รจ il miglior candidato fra i nostri componenti che si occuperร di mantenere al suo interno tale oggetto. Sarร questo componente lโunico ad avere la responsabilitร di aggiornare lโoggetto state attraverso lโinvocazione della funzione setState().
I due componenti SearchBar e ContactList hanno bisogno di usare il testo digitato nel campo di ricerca il quale rappresenta lโunica informazione dinamica della nostra applicazione. Entrambi sono racchiusi allโinterno del componente App che รจ quindi il candidato ideale per mantenere lโoggetto state. Il componente App invocherร il metodo setState() per aggiornare il valore di state.campoDiRicerca e passerร il nuovo valore ai componenti figli attraverso lโoggetto props. Quando questi riceveranno il nuovo valore, verrร invocata la loro funzione render() che provvederร al loro aggiornamento.
Riprenderemo lโesempio appena discusso nei prossimi articoli. A partire dal prossimo, vedremo come creare strutture piรน complesse e annidare i vari componenti.