React.js: identificazione di input diversi con un gestore onChange


142

Curioso quale sia il modo giusto di affrontarlo:

var Hello = React.createClass({
getInitialState: function() {
    return {total: 0, input1:0, input2:0};
},
render: function() {
    return (
        <div>{this.state.total}<br/>
            <input type="text" value={this.state.input1} onChange={this.handleChange} />
            <input type="text" value={this.state.input2} onChange={this.handleChange} />
        </div>
    );
},
handleChange: function(e){
    this.setState({ ??? : e.target.value});
    t = this.state.input1 + this.state.input2;
    this.setState({total: t});
}
});

React.renderComponent(<Hello />, document.getElementById('content'));

Ovviamente potresti creare funzioni handleChange separate per gestire ogni input diverso, ma non è molto carino. Allo stesso modo potresti creare un componente solo per un singolo input, ma volevo vedere se c'è un modo per farlo in questo modo.

Risposte:


163

Suggerisco di attenermi agli attributi HTML standard come namesu inputElements per identificare i tuoi input. Inoltre, non è necessario mantenere "totale" come valore separato nello stato perché è compostabile aggiungendo altri valori nel proprio stato:

var Hello = React.createClass({
    getInitialState: function() {
        return {input1: 0, input2: 0};
    },
    render: function() {
        const total = this.state.input1 + this.state.input2;

        return (
            <div>{total}<br/>
                <input type="text" value={this.state.input1} name="input1" onChange={this.handleChange} />
                <input type="text" value={this.state.input2} name="input2" onChange={this.handleChange} />
            </div>
        );
    },
    handleChange: function(e) {
        this.setState({[e.target.name]: e.target.value});
    }
});

React.renderComponent(<Hello />, document.getElementById('content'));

3
Inizialmente stavo provando a farlo, ma setState ({e.target.name ... non funziona, è un errore di sintassi. Devi creare un oggetto temp con obj [e.target.name] e setState a quello. Questo tipo di funzionamento funziona, ma sembra che ci sia una sorta di ritardo nell'aggiornamento dell'oggetto stato.
T3db0t

1
Il "tipo di ritardo" è impostato Aggiornamento dello stato durante requestAnimationFrame (presumo), quindi ignora tale osservazione
T3db0t

24
@ T3db0t Questa sintassi ES6 funziona:this.setState({ [e.target.name]: e.target.value });
XåpplI'-I0llwlg'I -

2
L'uso di bind è l'approccio migliore. Questo approccio non funziona se si collega il gestore onChange a un elemento senza una proprietà name, come un div.
Ericgrosse,

3
In questo caso il bind @ericgrosse non è migliore in quanto stai creando molte funzioni non necessarie. inoltre, potresti usare un attributo di dati o qualcosa su un diverso tipo di elemento se necessario
aw04

106

È possibile utilizzare il .bindmetodo per precompilare i parametri sul handleChangemetodo. Sarebbe qualcosa del tipo:

  var Hello = React.createClass({
    getInitialState: function() {
        return {input1:0, 
                input2:0};
    },
    render: function() {
      var total = this.state.input1 + this.state.input2;
      return (
        <div>{total}<br/>
          <input type="text" value={this.state.input1} 
                             onChange={this.handleChange.bind(this, 'input1')} />
          <input type="text" value={this.state.input2} 
                             onChange={this.handleChange.bind(this, 'input2')} />
        </div>
      );
    },
    handleChange: function (name, e) {
      var change = {};
      change[name] = e.target.value;
      this.setState(change);
    }
  });

  React.renderComponent(<Hello />, document.getElementById('content'));

(Ho anche fatto totalcalcolare al momento del rendering, in quanto è la cosa consigliata da fare.)


13
Solo un rapido promemoria che this.handleChange.bind(...)crea una nuova funzione. Nel caso in cui stai usando shouldComponentUpdatecon PureRenderMixino qualcosa di simile si romperà. Tutti i componenti di input verranno restituiti quando cambia un singolo valore.
zemirco,

5
Se cambi l'implementazione di handleChangea handleChange(name) { return event => this.setState({name: event.target.value}); }puoi passare la "stessa" funzione (non creerai una nuova funzione come viene fatta usando .bind()Allora, puoi passare tale funzione:this.handleChange("input1")
Andreyco,

1
Penso che sia anche importante ricordare che la natura di setState è che non si sarà in grado di recuperare lo stato corrente nella funzione handChange, ma lo stato precedente usando, ad esempio, this.state.input1. Un approccio adeguato sarà quello di creare un callback come this.setState(change, function () { console.log(this.state.input1); );riferimento: stackoverflow.com/questions/30782948/…
Charlie-Greenman

39

L' onChangeevento bolle ... Quindi puoi fare qualcosa del genere:

// A sample form
render () {
  <form onChange={setField}>
    <input name="input1" />
    <input name="input2" />
  </form>
}

E il tuo metodo setField potrebbe assomigliare a questo (supponendo che stai usando ES2015 o successivo:

setField (e) {
  this.setState({[e.target.name]: e.target.value})
}

Uso qualcosa di simile a questo in diverse app ed è abbastanza utile.


11

Soluzione obsoleta

valueLink/checkedLinksono deprecati dal core React, perché confonde alcuni utenti. Questa risposta non funzionerà se si utilizza una versione recente di React. Ma se ti piace, puoi facilmente emularlo creando il tuo Inputcomponente

Contenuto della risposta precedente:

Ciò che desideri ottenere può essere ottenuto molto più facilmente utilizzando gli helper a 2 vie di associazione dei dati di React.

var Hello = React.createClass({
    mixins: [React.addons.LinkedStateMixin],
    getInitialState: function() {
        return {input1: 0, input2: 0};
    },

    render: function() {
        var total = this.state.input1 + this.state.input2;
        return (
            <div>{total}<br/>
                <input type="text" valueLink={this.linkState('input1')} />;
                <input type="text" valueLink={this.linkState('input2')} />;
            </div>
        );
    }

});

React.renderComponent(<Hello />, document.getElementById('content'));

Facile vero?

http://facebook.github.io/react/docs/two-way-binding-helpers.html

Puoi persino implementare il tuo mixin


Perché dobbiamo associare uno scambio per impostare lo stato. Dopotutto è ReactJS!
Junaid Qadir,

Si noti che questa soluzione è obsoleta
Philipp

6

Puoi anche farlo in questo modo:

...
constructor() {
    super();
    this.state = { input1: 0, input2: 0 };
    this.handleChange = this.handleChange.bind(this);
}

handleChange(input, value) {
    this.setState({
        [input]: value
    })
}

render() {
    const total = this.state.input1 + this.state.input2;
    return (
        <div>
            {total}<br />
            <input type="text" onChange={e => this.handleChange('input1', e.target.value)} />
            <input type="text" onChange={e => this.handleChange('input2', e.target.value)} />
        </div>
    )
}

non ha funzionato per me. ha visto gli strumenti di sviluppo di React. la sua creazione e assegnazione di valori a variabile / oggetto chiamato input, anziché input1 / input2
Gaurravs

1
@Gaurravs si hai ragione. Avevo bisogno di aggiungere []in giro inputin setState. Ciò rende dinamicamente la proprietà assegnata
inostia il

4

Puoi usare un Reactattributo speciale chiamato refe quindi abbinare i veri nodi DOM onChangenell'evento usando Reactla getDOMNode()funzione di:

handleClick: function(event) {
  if (event.target === this.refs.prev.getDOMNode()) {
    ...
  }
}

render: function() {
  ...
  <button ref="prev" onClick={this.handleClick}>Previous question</button>
  <button ref="next" onClick={this.handleClick}>Next question</button>
  ...
}

Almeno in 0.13 this.refs non ha un pref della proprietà.
blub

2
@usagidon - prevè solo un nome che ho impostato nel render()metodo
Janusz Kacalak il

2
A partire da React v0.14, puoi semplicemente farlo event.target === this.refs.prev. facebook.github.io/react/blog/2015/10/07/…
XåpplI'-I0llwlg'I -

0

@Vigril Disgr4ce

Quando si tratta di moduli multi-campo, ha senso usare la caratteristica chiave di React: i componenti.

Nei miei progetti, creo componenti TextField, che prendono almeno un valore prop e si occupa della gestione dei comportamenti comuni di un campo di testo di input. In questo modo non devi preoccuparti di tenere traccia dei nomi dei campi durante l'aggiornamento dello stato del valore.

[...]

handleChange: function(event) {
  this.setState({value: event.target.value});
},
render: function() {
  var value = this.state.value;
  return <input type="text" value={value} onChange={this.handleChange} />;
}

[...]

0

È possibile tenere traccia del valore di ciascun figlio inputcreando un InputFieldcomponente separato che gestisce il valore di un singolo input. Ad esempio InputFieldpotrebbe essere:

var InputField = React.createClass({
  getInitialState: function () {
    return({text: this.props.text})
  },
  onChangeHandler: function (event) {
     this.setState({text: event.target.value})
  }, 
  render: function () {
    return (<input onChange={this.onChangeHandler} value={this.state.text} />)
  }
})

Ora il valore di ciascuno inputpuò essere tracciato all'interno di un'istanza separata di questo InputFieldcomponente senza creare valori separati nello stato del genitore per monitorare ciascun componente figlio.


0

Fornirò una soluzione davvero semplice al problema. Supponiamo di avere due input usernamee password, ma vogliamo che il nostro handle sia semplice e generico, quindi possiamo riutilizzarlo e non scrivere il codice del boilerplate.

I.La nostra forma:

                <form>
                    <input type="text" name = "username" onChange={this.onChange} value={this.state.username}/>
                    <input type="text" name = "password" onChange={this.onChange} value={this.state.password}/>
                    <br></br>
                    <button type="submit">Submit</button>
                </form>

II.Il nostro costruttore, che vogliamo salvare il nostro usernamee password, in modo che possiamo accedervi facilmente:

constructor(props) {
    super(props);
    this.state = {
        username: '',
        password: ''
    };

    this.onSubmit = this.onSubmit.bind(this);
    this.onChange = this.onChange.bind(this);
}

III. L'handle interessante e "generico" con un solo onChangeevento si basa su questo:

onChange(event) {
    let inputName = event.target.name;
    let value = event.target.value;

    this.setState({[inputName]:value});


    event.preventDefault();
}

Lasciatemi spiegare:

1.Quando viene rilevata una modifica, onChange(event)viene chiamato

2.Quindi otteniamo il parametro name del campo e il suo valore:

let inputName = event.target.name; ex: username

let value = event.target.value; ex: itsgosho

3. Sulla base del parametro name otteniamo il nostro valore dallo stato nel costruttore e lo aggiorniamo con il valore:

this.state['username'] = 'itsgosho'

4. La chiave da notare qui è che il nome del campo deve corrispondere al nostro parametro nello stato

Spero di aver aiutato qualcuno in qualche modo :)


0

La chiave del tuo stato dovrebbe essere la stessa del nome del tuo campo di input. Quindi puoi farlo nel metodo handleEvent;

this.setState({
        [event.target.name]: event.target.value
});

-1

Hi hanno migliorato ssorallen risposta. Non è necessario associare la funzione perché è possibile accedere all'input senza di essa.

var Hello = React.createClass({
    render: function() {
        var total = this.state.input1 + this.state.input2;
        return (
             <div>{total}<br/>
                  <input type="text" 
                    value={this.state.input1}
                    id="input1"  
                    onChange={this.handleChange} />
                 <input type="text" 
                    value={this.state.input2}
                    id="input2" 
                    onChange={this.handleChange} />
            </div>
       );
   },
   handleChange: function (name, value) {
       var change = {};
       change[name] = value;
       this.setState(change);
   }
});

React.renderComponent(<Hello />, document.getElementById('content'));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.