Forms
Gli elementi HTML form
funzionano in un modo differente rispetto agli altri elementi DOM in React, la motivazione sta nel fatto che gli elementi form mantengono naturalmente uno stato interno. Ad esempio, questo form in puro HTML accetta un singolo nome:
<form>
<label>
Nome:
<input type="text" name="nome" />
</label>
<input type="submit" value="Submit" />
</form>
Questo form si comporta come di consueto, facendo navigare l’utente in una nuova pagina quando viene inviato. In React, se vuoi avere lo stesso comportamento, non c’è bisogno di fare alcuna modifica. Ad ogni modo, potrebbe essere più conveniente avere una funzione JavaScript che gestisce l’invio del form e che ha accesso ai dati inseriti dall’utente. La tecnica standard con cui si può ottenere ciò prende il nome di “componenti controllati”.
Componenti Controllati
In HTML, gli elementi di un form come <input>
, <textarea>
e <select>
mantengono tipicamente il proprio stato e lo aggiornano in base all’input dell’utente. In React, lo stato mutabile viene tipicamente mantenuto nella proprietà state
dei componenti e viene poi aggiornato solo mediante setState()
.
Possiamo combinare le due cose rendendo lo state in React la “singola fonte attendibile” (SSOT). Possiamo poi fare in modo che il componente React che renderizza il form controlli anche cosa succede all’interno del form in risposta agli input dell’utente. In un form, un elemento di input il cui valore è controllato da React in questo modo viene chiamato “componente controllato”.
Ad esempio, se vogliamo far sì che l’esempio precedente registri il nome inserito, possiamo riscrivere il form sotto forma di componente controllato:
class FormNome extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) { this.setState({value: event.target.value}); }
handleSubmit(event) {
alert('E\' stato inserito un nome: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}> <label>
Nome:
<input type="text" value={this.state.value} onChange={this.handleChange} /> </label>
<input type="submit" value="Submit" />
</form>
);
}
}
Dato che l’attributo value
viene impostato nel nostro elemento form, il valore visualizzato sarà sempre this.state.value
, rendendo lo stato in React l’unica fonte di dati attendibile. Dato che la funzione handleChange
viene eseguita ad ogni battitura per aggiornare lo stato di React, il valore visualizzato verrà aggiornato man mano che l’utente preme i tasti.
Con un componente controllato, il valore dell’input viene sempre controllato dallo stato di React. Anche se ciò comporta la battitura di più codice, permette il passaggio del valore anche ad altri elementi della UI, o di resettarlo da altri event handlers.
Il Tag Textarea
In HTML, l’elemento <textarea>
definisce il testo in esso contenuto con i suoi elementi figli:
<textarea>
Nel mezzo del cammin di nostra vita
mi ritrovai per una selva oscura
ché la diritta via era smarrita.
</textarea>
In React, invece, <textarea>
utilizza l’attributo value
. Per questo, un form che utilizza una <textarea>
può essere scritto in modo molto simile a come verrebbe scritto se utilizzasse un semplice input di una sola riga:
class FormTema extends React.Component {
constructor(props) {
super(props);
this.state = { value: 'Per favore scrivi un tema riguardo il tuo elemento DOM preferito.' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) { this.setState({value: event.target.value}); }
handleSubmit(event) {
alert('Un tema è stato inviato: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Tema:
<textarea value={this.state.value} onChange={this.handleChange} /> </label>
<input type="submit" value="Submit" />
</form>
);
}
}
Nota come this.state.value
viene inizializzato nel costruttore, cosìcche la casella di testo è inizializzata con del testo al suo interno.
Il Tag Select
In HTML, <select>
crea una lista a discesa. Per esempio, questo HTML crea una lista a discesa di gusti:
<select>
<option value="pompelmo">Pompelmo</option>
<option value="limone">Limone</option>
<option selected value="cocco">Cocco</option>
<option value="mango">Mango</option>
</select>
Nota come l’opzione Cocco venga preselezionata grazie all’attributo selected
. React, piuttosto che usare l’attributo selected
, usa l’attributo value
dell’elemento radice select
. Ciò facilita le cose in un componente controllato in quanto bisogna aggiornare lo stato in un posto solo. Ad esempio:
class FormGusti extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'cocco'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) { this.setState({value: event.target.value}); }
handleSubmit(event) {
alert('Il tuo gusto preferito è: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Seleziona il tuo gusto preferito:
<select value={this.state.value}
onChange={this.handleChange}>
<option value="pompelmo">Pompelmo</option>
<option value="limone">Limone</option>
<option value="cocco">Cocco</option>
<option value="mango">Mango</option>
</select>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Ricapitolando, ciò fa sì che <input type="text">
, <textarea>
e <select>
funzionino in modo molto simile - tutti accettano un attributo value
che puoi utilizzare per implementare un componente controllato.
Nota bene
Puoi passare un array nell’attributo
value
, permettendoti di selezionare opzioni multiple in un tagselect
:<select multiple={true} value={['B', 'C']}>
Il Tag Input File
In HTML, un <input type="file">
permette all’utente di selezionare uno o più file da disco e di inviarli al server o manipolarli in JavaScript mediante le File API.
<input type="file" />
Dato che il suo valore è in sola-lettura, è un componente non controllato in React. Riprenderemo il discorso riguardo questo ed altri componenti non controllati in seguito.
Gestione di Input Multipli
Quando devi gestire diversi elementi input
, puoi aggiungere un attributo name
ad ognuno di essi e far sì che la funzione handler controlli cosa fare in base al valore di event.target.name
.
Ad esempio:
class Prenotazione extends React.Component {
constructor(props) {
super(props);
this.state = {
presente: true,
numeroOspiti: 2,
};
this.handleInputChange = this.handleInputChange.bind(
this
);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value, });
}
render() {
return (
<form>
<label>
Sarà presente:
<input
name="presente"
type="checkbox"
checked={this.state.presente}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Numero di ospiti:
<input
name="numeroOspiti"
type="number"
value={this.state.numeroOspiti}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
Nota come abbiamo utilizzato la sintassi ES6 computed property name (“nome proprietà calcolato”) per aggiornare la rispettiva chiave nello stato a seconda dell’attributo name
dell’input:
this.setState({
[name]: value});
Il che in ES5 corrisponde al codice:
var statoParziale = {};
statoParziale[name] = value;this.setState(statoParziale);
Inoltre, dato che setState()
unisce uno stato parziale nello stato corrente automaticamente, dobbiamo chiamarla con le sole parti modificate.
Valore Null in Input Controllati
Specificare la prop value
in un componente controllato fa sì che l’utente possa cambiare l’input solo quando lo desideri. Se hai specificato un value
ma l’input è ancora editabile, potresti aver accidentalmente impostato value
come undefined
o null
.
Il codice seguente lo dimostra. (L’input è inizialmente bloccato ma diventa editabile dopo un secondo)
ReactDOM.createRoot(mountNode).render(<input value="ciao" />);
setTimeout(function() {
ReactDOM.createRoot(mountNode).render(<input value={null} />);
}, 1000);
Alternative ai Componenti Controllati
Utilizzare componenti controllati può sembrare laborioso a volte, soprattutto perché è necessario scrivere un event handler per ogni modo in cui i tuoi dati possono cambiare e perché si deve collegare lo stato di tutti gli input a quello di un componente React. Il tutto diventa particolarmente noioso quando bisogna convertire progetti preesistenti in React, o integrare un’applicazione React con una libreria non-React. In queste situazioni, si potrebbe ricorrere ai componenti non controllati, una tecnica alternativa per implementare forms ed i relativi campi di input.
Soluzioni Chiavi In Mano
Se stai cercando una soluzione che include la validazione dei dati, il tener traccia dei campi visitati e la sottomissione del form, Formik è una delle scelte popolari. Comunque, si basa sugli stessi principi dei componenti controllati e della gestione dello stato — ecco perché è bene essere familiari con questi concetti.