Ciclo di vita dei componenti

13 settembre 2016

I componenti giocano un ruolo centrale nello sviluppo di applicazioni con React perché è grazie alle combinazioni tra componenti che possiamo costruire le interfacce utente, a partire dalle parti più piccole fino ad arrivare alla pagina completa.

Nelle lezioni precedenti abbiamo creato un componente di esempio per analizzarne la struttura, implementando l’unico metodo richiesto da React, render().

Come abbiamo visto, il metodo render() ha la responsabilità di restituire gli elementi che rappresentano graficamente il componente utilizzando le funzioni della libreria React, direttamente o indirettamente (tramite la sintassi JSX). React, nella fase di rendering, dovrà “montare” tali elementi del Virtual DOM nella pagina modificando gli oggetti del DOM reale.

In questa lezione approfondiremo il ciclo di vita dei componenti React e i metodi da implementare al suo interno per gestirne le fasi. Lo faremo realizzando anche stavolta un componente di esempio.

Scheletro iniziale del componente

Supponiamo di voler creare un componente che rappresenti un cronometro. L’interfaccia del componente includerà un pulsante per azzerare il conteggio e un etichetta che mostra il tempo trascorso (espresso in secondi) in grassetto.

Il primo passo da compiere consiste nella definizione dello scheletro del componente, creando l’oggetto che lo rappresenta e inserendo la funzione render() che ne gestisce il rendering:

render: function () {
	return (
		<div className="well">
            <button type="button" title="Clic per resettare il timer" onClick={this.resetCounter}>Reset</button>
            &nbsp;
            <span className="glyphicon glyphicon-time"></span>
            &nbsp;
            <strong>{this.state.counter}</strong>
        </div>
    );
}

Nell’esempio, il metodo render() restituisce un elemento <div> che funge da contenitore e ospita al suo interno il pulsante per il reset del cronometro, una semplice icona a forma di orologio e il contatore del tempo trascorso, creati usando la sintassi JSX che sarà automaticamente trasformata dal “transpiler” in chiamate alla funzione React.createElement().

Gli elementi sono stati decorati con i nomi di classi CSS del framework Bootstrap allo scopo di dare loro una rappresentazione grafica più gradevole e mostrare come è possibile combinare React con altre librerie, sia grafiche sia funzionali, per costruire la propria applicazione.

Il codice presenta alcuni elementi racchiusi tra parentesi graffe {}. La prima occorrenza ci permette di assegnare una funzione per gestire l’evento del click sul pulsante di reset:

onClick={this.resetCounter}

il secondo recupera il valore del contatore dallo stato del componente.

{this.state.counter}

Inizializzazione e modifica dello stato

In una precedente lezione abbiamo visto come sia possibile introdurre lo stato all’interno di un componente e come esso mantenga i valori che il componente ha la facoltà di mutare, a differenza delle proprietà, lasciando a React il compito di eseguire automaticamente il rendering e aggiornare la pagina a fronte di un cambiamento.

Nel nostro esempio, il conteggio dei secondi viene letto dal campo counter dell’oggetto state, che andrà a incrementarsi con il trascorrere del tempo.

È responsabilità dello sviluppatore fornire a React l’oggetto che rappresenta lo stato iniziale del componente, implementando la funzione getInitialState(). Per questo la aggiungiamo alla nostra implementazione.

getInitialState: function () {
    return { counter: 0 }
}

L’oggetto restituito rappresenta lo stato iniziale accessibile tramite state, che è l’identificatore con cui possiamo accedere all’oggetto che rappresenta lo stato e leggerne i valori, oppure modificarli.

Quando viene premuto il pulsante di reset, ad esempio, dovremo riportare il valore di state.counter a zero per far ripartire il conteggio. React si accorgerà della modifica e aggiornerà la pagina per mostrare il nuovo valore.

Nel metodo render() abbiamo associato la funzione resetCounter() al click sul pulsante, quindi la aggiungiamo:

resetCounter: function () {
  this.setState({ counter: 0 });
}

Qui vediamo un esempio di chiamata della funzione setState() fornita da React “di serie” a ciascun componente e che consente di specificare un oggetto contenente i nuovi valori dello stato, in questo caso il contatore impostato a zero.

La funzione resetCounter() può essere vista come un “metodo privato” del componente e può essere affiancata alle funzioni già analizzate. Possiamo aggiungere quante funzioni private vogliamo per incapsulare logica di business o di gestione degli eventi, ed esse non disturberanno React fino a quando il nome attribuito loro non coincide con uno di quelli “riservati” dalla libreria alla gestione del ciclo di vita del componente.

Montaggio del componente nella pagina

React consente di intercettare il momento in cui un componente viene “montato” nella pagina, ossia quando la libreria ha provveduto a materializzare concretamente gli elementi del componente all’interno nella pagina, creando gli oggetti del DOM reale a partire dagli elementi restituiti da render() e basati sullo stato iniziale.

Quando il montaggio è completo, possiamo stare certi che l’interfaccia è presente nella pagina e possiamo quindi procedere con le operazioni che consentono di modificarla a fronte della variazione dello stato del componente.

Analogamente, la fase di “smontaggio” avviene quando, in base alla business logic, alle proprietà e allo stato dei componenti dell’interfaccia utente, gli oggetti del DOM vengono rimossi dalla pagina.

React ci consente di inserire due funzioni nel componente che verranno richiamate nelle fasi appena citate:

  • componentDidMount()
  • componentWillUnmount()

Si tratta di funzioni che rivestono un ruolo importante: sono i punti strategici in cui è possibile inizializzare (e finalizzare) eventuali oggetti dell’ambiente di runtime, o di altre librerie, per integrarle con il nostro componente.

Realizzare un componente “cronometro”

Per la realizzazione del nostro cronometro, possiamo sfruttare la funzione componentDidMount() per attivare il “timer” per la scansione del tempo:

componentDidMount: function () {
    this.tickInterval = setInterval(this.doTick, 1000);
}

Utilizziamo la funzione JavaScript setInterval() per far invocare al browser una funzione doTick() ogni secondo (1000 ms). La funzione ci restituisce un riferimento, che memorizziamo nella proprietà tickInterval dell’oggetto che rappresenta il componente e ci servirà per arrestare il timer quando il componente verrà smontato. Non salviamo il riferimento all’interno dello stato in quanto il suo valore non cambia per l’intero ciclo di vita del componente, e soprattutto non dobbiamo utilizzare tale valore nel rendering.

La funzione doTick() invocata ogni secondo avrà il compito di incrementare il numero di secondi trascorsi e sarà implementata come segue:

doTick: function () {
    this.setState({ counter: this.state.counter + 1 });
}

Quando il componente viene “smontato” dalla pagina e non è più utilizzato, dobbiamo farci carico di liberare le risorse allocate, come il timer attivato con setInterval(); in caso contrario, oltre ad occupare memoria inutilmente, potremmo registrare comportamenti inaspettati all’interno del browser, compromettendo la stabilità della nostra applicazione.

Dobbiamo quindi “distruggere” il timer usando la funzione clearInterval() e passando il valore salvato nella proprietà tickInterval appena prima che React vada a smontare il componente dalla pagina:

componentWillUnmount: function () {
    clearInterval(this.tickInterval);
}

Le funzioni che abbiamo esaminato sono quelle che ricorrono con maggior frequenza e gestiscono le fasi fondamentali del ciclo di vita dei componenti. React fornisce tuttavia una gamma più estesa di funzioni, benché alcune di queste siano riservate a usi molto particolari, come la notifica del rendering imminente, che si possono in genere gestire attraverso lo stato del componente o con i meccanismi tradizionali di React.

Ecco infine il codice completo del componente:

var Timer = React.createClass({
    
	doTick: function () {
        this.setState({ counter: this.state.counter + 1 });
    },
    resetCounter: function () {
      this.setState({ counter: 0 });
    },
    componentDidMount: function () {
        this.tickInterval = setInterval(this.doTick, 1000);
    },
    componentWillUnmount: function () {
        clearInterval(this.tickInterval);
    },
    getInitialState: function () {
        return { counter: 0 }
    },
    render: function () {
        return (
            <div className="well">
                <button type="button" 
                    title="Clic per resettare il timer"
                    onClick={this.resetCounter}>Reset
                  </button>
                &nbsp;
                <span className="glyphicon glyphicon-time"></span>
                &nbsp;
                <strong>{this.state.counter}</strong>
            </div>
        );
    }
});

ReactDOM.render(<Timer />, document.getElementById('container'));

Tutte le lezioni

1 ... 12 13 14 ... 20

Se vuoi aggiornamenti su Ciclo di vita dei componenti inserisci la tua e-mail nel box qui sotto:
 
X
Se vuoi aggiornamenti su Ciclo di vita dei componenti

inserisci la tua e-mail nel box qui sotto:

Ho letto e acconsento l'informativa sulla privacy

Acconsento al trattamento di cui al punto 3 dell'informativa sulla privacy