back to top

Lavorare con Input e Form

In React è possibile usare dei campi di input e form simili a quelli a cui siamo abituati in HTML. Alcuni elementi sono implementati diversamente rispetto ai corrispettivi elementi HTML al fine di migliorare la compatibilità e la facilità di integrazione con il resto dell’applicazione React. Vedremo per esempio che sia l’elemento <textarea> che l’elemento <select> presentano delle piccole differenze a cui dobbiamo prestare attenzione.

Controlled Component vs. Uncontrolled Component

React permette di utilizzare gli elementi <input> in due modi differenti, come Controlled Input o Uncontrolled Input. Vediamo qual è la differenza.

Uncontrolled Component

Gli elementi <input> che vengono definiti "Uncontrolled" si comportano come gli elementi HTML a cui siamo abituati. Consideriamo per semplicità il caso di un <input> di tipo "text". Le considerazioni che faremo varranno anche per gli altri tipi di <input> e <textarea>. Quando digitiamo del testo all’interno del "campo testo", sarà quest’ultimo a memorizzare e a mantenere ogni volta il suo valore aggiornato. Potremo quindi recuperare il valore all’interno dell’elemento <input> utilizzando per esempio la proprietà ref che abbiamo già visto nell’articolo precedente.

Nell’esempio sottostante, creiamo un componente App. Nel metodo render() abbiamo inserito un elemento <input> di tipo "Uncontrolled" e un elemento <label> (Notate che nella label abbiamo usato l’attributo htmlFor al posto di "for". JSX viene convertito in semplice Javascript da Babel e "for" è una keyword riservata in Javascript). Il nome che digiteremo comparirà direttamente nel campo e sarà accessibile attraverso this.input.value (Abbiamo salvato in this.input un riferimento all’elemento DOM grazie all’uso di ref, come già visto nell’articolo precedente).

// Esempio 1
class App extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div>
        {/* Nota htmlFor al posto di for */}
        <label htmlFor="nome">Inserisci il tuo nome</label>
        <input type="text" 
          ref={input => {this.input = input}} 
          id="nome" 
          placeholder="Nome"
          onBlur={() => console.log(this.input.value)}
        />
      </div>
    )
  }
}
esempio uso uncontrolled component react

Ogni volta che premiamo un tasto, un nuovo carattere sarà mostrato all’interno del "campo testo" e, quando questo perde il focus, stampiamo il contenuto nella console.

Nel caso appena visto non è possibile definire un attributo value, capiremo a breve perché. Se abbiamo bisogno di specificare un valore iniziale dovremo usare l’attributo defaultValue.

<input 
  type="text" 
  ref={input => {this.input = input}} 
  defaultValue="Valore Iniziale"
/>

In alcuni contesti può risultare vantaggioso usare questo tipo di componenti anche se non ci permettono di sfruttare in pieno le potenzialità di React.

Controlled Component

Vengono definiti "Controlled", gli elementi <input> il cui valore corrente viene impostato tramite l’oggetto Props. In questo caso, <input> non mantiene alcuno stato interno e si limiterà a mostrare il valore passatogli. Nel caso di un elemento <input> di tipo "text", passeremo il valore da mostrare tramite l’attributo value il quale è l’attributo che rende <input> un "Controlled Component".

Cerchiamo di capire meglio di cosa si tratta attraverso un esempio.

// Esempio 2
class App extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div>
        {/* Nota htmlFor al posto di for */}
        <label htmlFor="nome">Inserisci il tuo nome</label>
        <input type="text"  value="" />
      </div>
    )
  }
}

Abbiamo un semplice <input> di tipo text su cui è definito un attributo value. In questo caso si tratta quindi di un "Controlled Input" perché abbiamo passato l’attributo value, attraverso il quale impostiamo il valore corrente di <input>. Questo si limiterà esclusivamente a mostrare il valore che gli viene passato e non terrà traccia di ciò che viene digitato al suo interno. Se infatti proviamo a digitare qualcosa all’interno del campo di testo, non viene aggiornato il suo valore. Questo comportamento è mostrato nell’immagine sottostate.

esempio uso controlled component react

Come è possibile vedere dall’immagine, nonostante abbiamo digitato delle lettere, il campo <input> resta vuoto o meglio continua a mostrare il valore che abbiamo passato tramite l’attributo value (value=""). Ciò accade perché <input> non mantiene traccia del valore che digitiamo. Essendo un’informazione mutevole, possiamo salvare il testo digitato all’interno dell’oggetto State del componente App. Aggiungeremo poi un attributo onChange all’elemento <input> a cui passiamo poi una funzione che verrà invocata ogni volta che digitiamo un carattere.

Riportiamo un’immagine che possa aiutare a visualizzare meglio il procedimento che useremo per modificare il codice dell’esempio 2 in cui sostituiremo l’input di tipo "Uncontrolled" con un <input> di tipo "Controlled".

schema di funzionamento controlled component react

Modifichiamo allora l’esempio 2 e vediamo come usare i Controlled Component.

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {nome: ""};
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.setState({nome: e.target.value});
  }

  render() {
    return (
      <div>
        {/* Nota htmlFor al posto di for */}
        <label htmlFor="nome">Inserisci il tuo nome</label>
        <input 
          type="text" 
          placeholder="Nome" 
          onChange={this.handleChange}  
          value={this.state.value}
        />
        <p>{this.state.nome}</p>
      </div>
    )
  }
}

Per prima cosa abbiamo inizializzato this.state nel costruttore e, come visto negli articoli precedenti, chiamiamo la funzione Function.prototype.bind() che restituirà una nuova funzione con lo stesso codice del metodo handleChange() ma all’interno della quale this punta all’istanza del componente App in cui handleChange() viene definita.

Nel metodo render() abbiamo inserito un elemento <input>. Ogni volta che digitiamo una lettera, verrà invocata handleChange() che a sua volta chiamerà this.setState(). Il nuovo valore di state.nome sarà quindi passato all’elemento input tramite l’attributo value e verrà mostrato all’interno del paragrafo sottostante il campo di testo. Ogni volta che invochiamo setState() verrà chiamata la funzione render() (come abbiamo visto quando abbiamo parlato del ciclo di vita di un componente)

Elementi JSX vs HTML

Abbiamo accennato sopra che, in React, alcuni elementi come <select> e <textarea> differiscono dai corrispettivi elementi HTML. L’elemento <textarea>, in particolare, usa un attributo value per definire il testo che deve essere mostrato al suo interno. Allo stesso modo dovremo usare un attributo value sull’elemento <select> per selezionare una delle opzioni disponibili. Mettiamo a confronto il codice scritto in JSX e HTML.

<!-- <textarea></textarea> in HTML -->
<textarea>Textarea in HTML</textarea>

<!-- <select></select> in HTML -->
<select>
  <option value="Mercedes">Mercedes</option>
  <option value="Red Bull">Red Bull</option>
  <option value="Ferrari">Ferrari</option>
</select>

In React invece passeremo due attributi value e onChange. Quest’ultimo ci permetterà di usare una funzione all’interno della quale aggiorneremo opportunamente il valore che sarà passato all’attributo value.

// <textarea /> in React utilizzando JSX
<textarea value={this.state.value} onChange={this.handleChange} />

// <select></select> in React utilizzando JSX
<select value={this.state.value} onChange={this.handleChange}>
  <option value="Mercedes">Mercedes</option>
  <option value="Red Bull">Red Bull</option>
  <option value="Ferrari">Ferrari</option>
</select>

Facciamo infine un esempio per ricapitolare quanto visto. Per semplicità trascureremo ogni forma di validazione, ottimizzazione e riorganizzazione del codice che sarebbero necessari qualora volessimo usare il form dell’esempio in un’applicazione reale.

Creeremo un form come quello mostrato nell’immagine sottostante.

Wireframe applicazione con form in react
class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      vinile: 'Album1',
      mail: '',
      pagamento: "Carta di credito",
      note: '',
      regalo: false,
      stato: ''
    };

    /*
    * Ogni volta che viene invocata this.setState(), 
    * React assegna a this.state il riferimento a
    * un nuovo oggetto. Dopo che viene invocata this.setState() per la prima volta, 
    * this.initialState !== this.state
    */
    this.initialState = this.state;

    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.clearForm = this.clearForm.bind(this);
  }
  handleChange(e) {
    const target = e.target;
    const valore = target.type === 'checkbox' ? target.checked : target.value;
    const proprieta = target.name;

    /* sintassi introdotta in ES6
    *  Computed property names
    *  https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names
    */
    this.setState({
      [proprieta]: valore
    });

  }
  handleSubmit(e) {
    e.preventDefault();
    this.setState({stato: "sent"}, () => window.setTimeout(this.clearForm,3000));
  }
  clearForm() {
    this.setState(this.initialState);
  }
  render() {
    return (
      <form onSubmit={this.handleSubmit}>

        <label htmlFor="vinile">Seleziona un vinile</label>
        <select name="vinile" id="vinile" value={this.state.vinile} onChange={this.handleChange}>
          <option value="Album1">Album 1</option>
          <option value="Album2">Album 2</option>
          <option value="Album3">Album 3</option>
        </select>

        <label htmlFor="mail">E-mail</label>
        <input 
          type="email" 
          id="mail" 
          name="mail" 
          value={this.state.mail} 
          onChange={this.handleChange}
        />
        <fieldset>
          <legend>Modalità di pagamento</legend>
          <label>
            <input 
              type="radio" 
              name="pagamento" 
              value="Carta di credito"
              onChange={this.handleChange}
              checked={this.state.pagamento === "Carta di credito"} />Carta di credito
          </label>
          <label>
            <input 
              type="radio" 
              name="pagamento" 
              value="Contrassegno"
              onChange={this.handleChange} 
              checked={this.state.pagamento === "Contrassegno"}/>Contrassegno
          </label>
        </fieldset>
        <label htmlFor="note">Note</label>
        <textarea name="note" id="note" value={this.state.note} onChange={this.handleChange} />
        <label>
          <input
            name="regalo"
            type="checkbox"
            checked={this.state.regalo}
            onChange={this.handleChange} />
            Si tratta di un regalo
        </label>
        <input type="submit" value="Prenota" />
        <p className={"message " + this.state.stato}>
          Grazie per aver prenotato.<br />
          Riceverai una mail di conferma a breve.
        </p>
      </form>
    )
  }
}
/* salviamo un riferimento ad un'istanza del componente App
*  che useremo per accedere al valore di app.initialState
*  nella console per sviluppatori
*/
const app = ReactDOM.render(<App />, rootNode);

Potete vedere il risultato finale nelle due immagini sottostanti.

esempio di form in react

Ogni volta che viene invocata this.setState(), React assegna a this.state il riferimento a un nuovo oggetto. Dopo che viene invocata this.setState() per la prima volta, this.initialState !== this.state.

semplice dimostrazione del funzionamento di un form in react

Nel prossimo articolo lasceremo per un attimo da parte React e parleremo di NPM.

Pubblicità