State dei componenti
React attribuisce a ciascun componente un insieme di dati chiamati stato interno. Questo stato è “privato”: nel senso che gli altri componenti non posso accedervi.
Al contrario delle props, può essere modificato (solo dal componente stesso). Inoltre, ogni sua modifica comporta un aggiornamento della visualizzazione del componente nella pagina Html.
Per accedervi si usa la parola chiave state.
Non si può modificare direttamente lo stato ma lo si deve usare obbligatoriamente tramite la 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.
// Non aggiornare mai 'state' direttamente, React non aggiornerà il componente this.state.counter = 1 // NO!
// Versione Corretta this.setState({counter: 1}); // OK
Esempio:
Creiamo una applicazione che simula il lancio di un dado, ossia una pagina html con un tasto. Ogni volta che il tasto viene premuto, si visualizza un numero da 1 a 6.
Useremo un unico componente, chiamato Dado. All’interno di questo componente, dobbiamo memorizzare il numero estratto. Questa informazione è perfetta per essere inserita nello stato del componente Dado. Dato che abbiamo bisogno di uno stato, useremo un class component.
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> ) } }
Provalo su CodePen
Nel costruttore del componente, invochiamo il costruttore dell’oggetto padre tramite il metodo super(props) . Se non facessimo ciò, non potremmo usare la keyword this all’interno del costruttore.
Lo state ha una sola proprietà che conterrà il numero estratto ad ogni lancio.
Nella funzione render() mostriamo un messaggio iniziale di benvenuto; dopodiché, verrà mostrato il numero estratto ad ogni lancio, ossia lo stato del componente.
Ogni volta che il pulsante viene premuto, 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().
Aggiornando lo stato viene richiamata in automatico la funzione render, che stampa il lo stato a video.
PROPRIETA DELLA FUNZIONE SETSTATE
React potrebbe raggruppare più chiamate della funzione setState() ed eseguire l’aggiornamento dell’oggetto state una volta sola. Quindi, l’aggiornamento degli oggetti state e props può avvenire in maniera asincrona. Per questo motivo, dovremmo usare un’altra versione di setState() che riceve due argomenti, ovvero i valori correnti degli oggetti state e props.
Questo codice, ad esempio, potrebbe non riuscire ad aggiornare correttamente il contatore:
// Sbagliato this.setState({ counter: this.state.counter + this.props.increment, });
Per effettuare correttamente questa operazione, bisogna usare la seconda forma di setState.
// Giusto this.setState((state, props) => ({ counter: state.counter + props.increment }));
Qui abbiamo utilizzato una arrow function, ma si puo utilizzare anche una funzione tradizionale:
// Giusto this.setState(function(state, props) { return { counter: state.counter + props.increment }; });
In React, si ha anche la possibilità, nel caso di un componente che presenta un oggetto state con più coppie chiave-valore, di aggiornare una sola di queste coppie. 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] }
Riassumendo, quindi abbiamo:
- lo state non va modificato direttamente
ma solo con la funzione setState per effettuare modifiche allo state di un componente. - Quando lo state viene modificato, viene chiamato il metodo render per aggiornare l’applicazione.
- Lo state va sempre valorizzato nel costruttore ed è l’unico posto dove si può modificare direttamente senza l’uso di setState
- Quando utilizziamo props e state insieme bisogna considerare che vengono aggiornati in maniera asincrona, quindi si deve usare l’altra versione di setState, quella che prende in input state e props.
- Si può aggiornare solo le proprietà dello stato che interessano. Le proprietà non modificate rimarranno com’erano.
Flusso unidirezionale di dati
Possiamo vedere la nostra applicazione come una gerarchia di componenti, generalmente annidati tra di loro. Non tutti dovranno avere uno stato interno; anzi, avremo la maggior parte dei componenti senza stato (stateless) Solitamente si ha qualche componente ai vertici che sarà responsabile di mantenere lo stato della nostra applicazione e passerà le informazioni giù ai componenti figli tramite props. Questo è spesso definito flusso di dati “top-down” (dall’alto verso il basso) o “unidirezionale”, nel senso che lo stato “scorre” dal componente possessore dello stato ai suoi figli.