In questa lezione esaminiamo la gestione dello stato dei componenti da parte di React, riprendendo gli esempi già fatti nelle lezioni precedenti ed estendendoli per gestire scenari più complessi.
Che cos'è lo stato?
React attribuisce a ciascun componente un oggetto JavaScript che rappresenta lo stato, accessibile tramite l'identificatore this.state.
L'oggetto appartiene al componente ed è "privato": non è quindi possibile accedervi da parte di altri componenti in maniera diretta, ma è possibile consentirne l'inizializzazione accettando valori in input tramite proprietà immutabili (this.props).
I valori memorizzati nell'oggetto state
possono essere usati per modificarne il comportamento logico oppure possono inseriti direttamente nella rappresentazione del componente sulla pagina. React è in grado di rilevare eventuali modifiche alle proprietà dell'oggetto aggiornando l'interfaccia utente di conseguenza.
Inizializzazione dello stato
Nelle lezioni precedenti abbiamo visto come inizializzare lo stato di un componente React:
getInitialState: function () {
return { counter: 0 }
}
La funzione getInitialState()
ha la responsabilità di restituire l'oggetto che rappresenta i valori dello stato iniziale del componente e viene richiamata da React al momento opportuno.
Nell'esempio indicato sopra, lo stato viene inizializzato inserendo una proprietà counter
impostata al valore letterale 0
(zero), ma possiamo anche fare in modo che il valore sia passato dall'esterno tramite proprietà props
e aggiungere allo stato tanti campi quanti sono quelli richiesti dalla logica specifica del componente da implementare.
Immaginiamo uno scenario più complesso per il nostro esempio: supponiamo di trasformare il componente "Timer" in un conto alla rovescia e di voler inizializzare il contatore a un valore di partenza specificato dall'esterno; inoltre, aggiungiamo al componente la capacità aumentare o diminuire il passo usato per decrementare il contatore.
La funzione getInitialState()
potrebbe essere modificata come segue:
getInitialState: function () {
return {
counter: parseInt(this.props.initialCounter),
step: parseInt(this.props.initialStep)
}
}
ReactDOM.render(<Timer initialCounter="1000" initialStep="1" />, document.getElementById('container'));
Va sottolineato l'uso della funzione parseInt()
che ha il compito di garantire che i valori delle proprietà specificate dall'esterno vengano convertite nel tipo di dato che ci aspettiamo di trattare, ovvero dei numeri interi; in caso contrario, potrebbero verificarsi degli effetti collaterali, ad esempio un concatenamento di stringhe al posto di una somma.
Utilizzare lo stato nel rendering
La logica di rendering del componente può utilizzare le informazioni contenute nello stato per operare scelte che influiscono sull'aspetto finale del componente, oppure inserire direttamente i valori delle proprietà all'interno della pagina.
Nel nostro esempio, la funzione render()
restituisce un semplice elemento <div>
che contiene gli elementi della nostra interfaccia: il valore attuale del conteggio alla rovescia prendendo il dato da this.state.counter
, in grassetto, assieme ad alcuni pulsanti per resettare il timer al valore iniziale, oppure aumentare o diminuire il passo del conto alla rovescia.
render: function () {
return (
<div>
<strong>{this.state.counter}</strong>
<button type="button" onClick={this.resetCounter}>Reset</button>
<button type="button" onClick={this.speedUp}>+</button>
<button type="button" onClick={this.speedDown}>-</button>
</div>
);
}
Modificare lo stato del componente, setState()
React "reagisce" alla variazione dello stato dei componenti, ma qual è il modo corretto per aggiornare lo stato quando l'utente compie delle azioni o al verificarsi di un evento?
Innanzitutto, non si deve modificare l'oggetto this.state direttamente, bensì utilizzare la funzione setState()
che ha appositamente questo scopo e che abbiamo già visto nelle precedenti lezioni.
La funzione accetta come parametro un semplice oggetto JavaScript che React utilizza per aggiornare lo stato in base ai valori specificati nelle proprietà dell'oggetto.
È importante precisare che React non sostituisce integralmente lo stato del componente con quello passato alla funzione setState()
, ma effettua una sorta di combinazione tra i due: questo ci permette di aggiornare lo stato specificando solamente le proprietà che sono cambiate, lasciando inalterate le altre e senza costringerci a specificarle tutte ogni volta che dobbiamo aggiornare lo stato.
Per esemplificare, ecco il codice delle funzioni agganciate agli eventi di "tick" del timer e di clic sui pulsanti che mostrano l'uso pratico della funzione setState()
:
doTick: function () {
var newCounter = this.state.counter;
if (newCounter <= 0) return;
newCounter -= this.state.step;
if (newCounter < 0) newCounter = 0;
this.setState({ counter: newCounter });
},
resetCounter: function () {
this.setState({ counter: this.props.initialCounter });
},
speedDown: function () {
if (this.state.step <= 0) return;
this.setState({ step: this.state.step - 1 });
},
speedUp: function () {
this.setState({ step: this.state.step + 1 });
},
Osservando il codice, è facile comprendere il principio di funzionamento di setState()
e notare come ogni volta l'oggetto passato come parametro contenga solo le proprietà dello stato che vogliamo aggiornare: questo riduce notevolmente la quantità di codice da scrivere per implementare il componente e lo rende molto più leggibile, mitigando il rischio di possibili errori.