State e Lifecycle
These docs are old and wonât be updated. Go to react.dev for the new React docs.
These new documentation pages teach modern React and include live examples:
Questa pagina introduce il concetto di state (stato) e lifecycle (ciclo di vita) in un componente React. Puoi trovare un riferimento dettagliato alle API dei componenti qui.
Considera lâesempio dellâorologio di una delle sezioni precedenti. In Renderizzare Elementi, abbiamo appreso solamente un modo per aggiornare la UI. Chiamiamo root.render()
per cambiare lâoutput renderizzato:
const root = ReactDOM.createRoot(document.getElementById('root'));
function tick() {
const element = (
<div>
<h1>Ciao, mondo!</h1>
<h2>Sono le {new Date().toLocaleTimeString()}.</h2>
</div>
);
root.render(element);}
setInterval(tick, 1000);
In questa sezione, apprenderemo come rendere il componente Clock
davvero riutilizzabile ed incapsulato. Esso si occuperĂ di impostare il proprio timer e di aggiornarsi ogni secondo.
Possiamo iniziare incapsulando lâaspetto dellâorologio:
const root = ReactDOM.createRoot(document.getElementById('root'));
function Clock(props) {
return (
<div> <h1>Ciao, mondo!</h1> <h2>Sono le {props.date.toLocaleTimeString()}.</h2> </div> );
}
function tick() {
root.render(<Clock date={new Date()} />);}
setInterval(tick, 1000);
Tuttavia, manca un requisito fondamentale: il fatto che Clock
imposti un timer ed aggiorni la propria UI ogni secondo dovrebbe essere un dettaglio implementativo di Clock
.
Idealmente, vorremmo scrivere il seguente codice una volta sola, ed ottenere che Clock
si aggiorni da solo:
root.render(<Clock />);
Per implementare ciò, abbiamo bisogno di aggiungere uno âstatoâ al componente Clock
.
Lo state (o stato) è simile alle props, ma è privato e completamente controllato dal componente.
Convertire una Funzione in una Classe
Puoi convertire un componente funzione come Clock
in una classe in cinque passaggi:
- Crea una classe ES6, con lo stesso nome, che estende
React.Component
. - Aggiungi un singolo metodo vuoto chiamato
render()
. - Sposta il corpo della funzione allâinterno del metodo
render()
. - Sostituisci
props
conthis.props
nel corpo del metodorender()
. - Rimuovi la dichiarazione della funzione rimasta vuota.
class Clock extends React.Component {
render() {
return (
<div>
<h1>Ciao, mondo!</h1>
<h2>Sono le {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
Clock
è ora definito da una classe, invece che da una funzione.
Il metodo render
viene invocato ogni volta che si verifica un aggiornamento, ma finchĂŠ renderizziamo <Clock />
nello stesso nodo del DOM, verrĂ utilizzata unâunica istanza della classe Clock
. Questo ci consente di utilizzare funzionalitĂ aggiuntive come il local state e i metodi del lifecycle del componente.
Aggiungere il Local State ad una Classe
Sposteremo date
dalle props allo state in tre passaggi:
- Sostituisci
this.props.date
conthis.state.date
nel metodorender()
:
class Clock extends React.Component {
render() {
return (
<div>
<h1>Ciao, mondo!</h1>
<h2>Sono le {this.state.date.toLocaleTimeString()}.</h2> </div>
);
}
}
- Aggiungi un costruttore di classe che assegna il valore iniziale di
this.state
:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()}; }
render() {
return (
<div>
<h1>Ciao, mondo!</h1>
<h2>Sono le {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
Nota come passiamo props
al costruttore di base:
constructor(props) {
super(props); this.state = {date: new Date()};
}
I componenti classe dovrebbero sempre chiamare il costruttore base passando props
come argomento.
- Rimuovi la prop
date
dallâelemento<Clock />
:
root.render(<Clock />);
In seguito ci occuperemo di aggiungere la parte di codice relativa al timer allâinterno del componente stesso.
Il risultato dovrebbe avere questo aspetto:
class Clock extends React.Component {
constructor(props) { super(props); this.state = {date: new Date()}; }
render() {
return (
<div>
<h1>Ciao, mondo!</h1>
<h2>Sono le {this.state.date.toLocaleTimeString()}.</h2> </div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);
Adesso, faremo in modo che Clock
imposti il proprio timer e si aggiorni ogni secondo.
Aggiungere Metodi di Lifecycle ad una Classe
Nelle applicazioni con molti componenti, è molto importante rilasciare le risorse occupate dai componenti quando questi vengono distrutti.
Nel nostro caso, vogliamo impostare un timer ogni volta che Clock
è renderizzato nel DOM per la prima volta. Questo è definito âmountingâ (âmontaggioâ) in React.
Vogliamo anche cancellare il timer ogni volta che il DOM prodotto da Clock
viene rimosso. Questo è definito âunmountingâ (âsmontaggioâ) in React.
Possiamo dichiarare alcuni metodi speciali nel componente classe per eseguire del codice quando il componente viene montato e smontato:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() { }
componentWillUnmount() { }
render() {
return (
<div>
<h1>Ciao, mondo!</h1>
<h2>Sono le {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
Questi metodi sono chiamati âmetodi del lifecycleâ (metodi del ciclo di vita).
Il metodo componentDidMount()
viene eseguito dopo che lâoutput del componente è stato renderizzato nel DOM. Ă un buon punto in cui impostare un timer:
componentDidMount() {
this.timerID = setInterval( () => this.tick(), 1000 ); }
Nota come salviamo lâID del timer direttamente in this
(this.timerID
).
Mentre this.props
viene impostato da React stesso e this.state
ha un significato speciale, sei libero di aggiungere altri campi alla classe se hai bisogno di salvare qualcosa che non partecipa al flusso dei dati (come lâID di un timer).
Ci occuperemo di cancellare il timer nel metodo del lifecycle componentWillUnmount()
:
componentWillUnmount() {
clearInterval(this.timerID); }
Infine, implementeremo un metodo chiamato tick()
che verrĂ invocato dal componente Clock
ogni secondo.
Il nuovo metodo utilizzerĂ this.setState()
per pianificare gli aggiornamenti al local state del componente:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() { this.setState({ date: new Date() }); }
render() {
return (
<div>
<h1>Ciao, mondo!</h1>
<h2>Sono le {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);
In questo modo lâorologio scatta ogni secondo.
Ricapitoliamo velocemente quello che sta succedendo e lâordine con cui i metodi sono invocati:
- Quando
<Clock />
viene passato aroot.render()
, React invoca il costruttore del componenteClock
. Dal momento cheClock
ha bisogno di mostrare lâora corrente, inizializzathis.state
con un oggetto che include lâora corrente. In seguito, aggiorneremo questo state. - In seguito, React invoca il metodo
render()
del componenteClock
. Questo è il modo in cui React apprende cosa dovrebbe essere visualizzato sullo schermo. React si occupa di aggiornare il DOM in modo da farlo corrispondere allâoutput della renderizzazione diClock
. - Quando lâoutput della renderizzazione di
Clock
viene inserito nel DOM, React invoca il metodo del lifecyclecomponentDidMount()
. Al suo interno, il componenteClock
chiede al browser di impostare un timer con cui invocare il metodotick()
del componente una volta al secondo. - Ogni secondo, il browser invoca il metodo
tick()
. Al suo interno, il componenteClock
pianifica un aggiornamento della UI invocandosetState()
con un oggetto che contiene la nuova ora corrente. Grazie alla chiamata asetState()
, React viene informato del fatto che lo state è cambiato e invoca di nuovo il metodorender()
per sapere che cosa deve essere mostrato sullo schermo. Questa volta,this.state.date
nel metodorender()
avrĂ un valore differente, di conseguenza lâoutput della renderizzazione includerĂ lâorario aggiornato. React aggiorna il DOM di conseguenza. - Se il componente
Clock
dovesse mai essere rimosso dal DOM, React invocherebbe il metodo del lifecyclecomponentWillUnmount()
ed il timer verrebbe cancellato.
Utilizzare Correttamente lo Stato
Ci sono tre cose che devi sapere a proposito di setState()
.
Non Modificare lo Stato Direttamente
Per esempio, questo codice non farebbe ri-renderizzare un componente:
// Sbagliato
this.state.comment = 'Hello';
Devi invece utilizzare setState()
:
// Giusto
this.setState({comment: 'Hello'});
Lâunico punto in cui puoi assegnare direttamente un valore a this.state
è nel costruttore.
Gli Aggiornamenti di Stato Potrebbero Essere Asincroni
React potrebbe accorpare piĂš chiamate a setState()
in un unico aggiornamento per migliorare la performance.
PoichĂŠ this.props
e this.state
potrebbero essere aggiornate in modo asincrono, non dovresti basarti sul loro valore per calcolare lo stato successivo.
Ad esempio, questo codice potrebbe non riuscire ad aggiornare correttamente il contatore:
// Sbagliato
this.setState({
counter: this.state.counter + this.props.increment,
});
Per effettuare correttamente questa operazione, bisogna utilizzare una seconda forma di setState()
che accetta in input una funzione invece che un oggetto. Quella funzione riceverĂ come primo argomento lo stato precedente e come secondo argomento le proprietĂ , aggiornate al momento in cui lâaggiornamento di stato è applicato:
// Giusto
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
Qui abbiamo utilizzato una arrow function, ma puoi utilizzare anche una funzione tradizionale:
// Giusto
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
Gli Aggiornamenti di Stato Vengono Applicati Tramite Merge
Quando chiami setState()
, React effettua il merge dellâoggetto che fornisci nello state corrente.
Ad esempio, il tuo state potrebbe contenere molte variabili indipendenti:
constructor(props) {
super(props);
this.state = {
posts: [], comments: [] };
}
A questo punto puoi aggiornarle indipendentemente con invocazioni separate del metodo setState()
:
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts });
});
fetchComments().then(response => {
this.setState({
comments: response.comments });
});
}
Quello che viene effettuato è uno âshallow mergeâ, quindi this.setState({comments})
lascia intatto this.state.posts
, ma sostituisce completamente this.state.comments
.
I Dati Fluiscono Verso il Basso
NĂŠ i componenti genitori nĂŠ i componenti figli possono sapere se un certo componente è âstatefulâ o âstatelessâ (cioè se è dotato o meno di stato) e non dovrebbero preoccuparsi del fatto di essere definiti come funzione o come classe.
Questa è la ragione per cui lo stato è spesso definito locale o incapsulato. Esso non è accessibile a nessun componente a parte quello a cui appartiene.
Un componente potrebbe decidere di passare il suo stato ai componenti figli sotto forma di props:
<FormattedDate date={this.state.date} />
Il componente FormattedDate
riceve date
nelle sue props e non può sapere se viene dallo state di Clock
, dalle proprietĂ di Clock
o se è stato inserito a mano:
function FormattedDate(props) {
return <h2>Sono le {props.date.toLocaleTimeString()}.</h2>;
}
Questo è spesso definito flusso di dati âtop-downâ (dallâalto verso il basso) o âunidirezionaleâ. In questo paradigma, lo stato è sempre posseduto da uno specifico componente, e tutti i dati o la UI derivati da quello stato possono influenzare solamente i componenti âpiĂš in bassoâ nellâalbero.
Se immagini un albero di componenti come una cascata di props, puoi pensare allo stato di ciascun componente come a una sorgente dâacqua aggiuntiva che si unisce alla cascata in un punto qualsiasi e flusice verso il basso insieme al resto dellâacqua.
Per mostrare che tutti i componenti sono davvero isolati, possiamo creare un componente App
che renderizza tre <Clock>
:
function App() {
return (
<div>
<Clock /> <Clock /> <Clock /> </div>
);
}
Ciascun Clock
imposta il proprio timer e si aggiorna indipendentemente dagli altri.
Nelle applicazioni React, il fatto che un componente sia stateful o stateless è considerato un dettaglio implementativo di quel componente, che potrebbe cambiare nel tempo. Puoi utilizzare componenti stateless allâinterno di componenti stateful, e viceversa.