React.js: identification de différentes entrées avec un seul gestionnaire onChange


142

Curieux de savoir quelle est la bonne façon d'aborder cela:

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'));

Évidemment, vous pouvez créer des fonctions handleChange distinctes pour gérer chaque entrée différente, mais ce n'est pas très agréable. De même, vous pouvez créer un composant uniquement pour une entrée individuelle, mais je voulais voir s'il existe un moyen de le faire comme ça.

Réponses:


163

Je suggère de s'en tenir aux attributs HTML standard comme namesur inputElements pour identifier vos entrées. De plus, vous n'avez pas besoin de conserver "total" en tant que valeur distincte dans l'état, car il peut être composé en ajoutant d'autres valeurs dans votre état:

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
J'essayais de le faire au départ, mais setState ({e.target.name ... ne fonctionne pas - c'est une erreur de syntaxe. Vous devez créer un objet temporaire avec obj [e.target.name] et setState pour cela. Cela fonctionne, mais il semble y avoir une sorte de retard dans la mise à jour de l'objet d'état.
T3db0t

1
Le "genre de retard" est mis à jour setState pendant requestAnimationFrame (je suppose), alors ignorez cette remarque
T3db0t

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

2
Utiliser bind est la meilleure approche. Cette approche ne fonctionne pas si vous attachez le gestionnaire onChange à un élément sans propriété de nom, tel qu'un div.
ericgrosse

3
@ericgrosse bind n'est pas mieux dans ce cas car vous créez beaucoup de fonctions inutiles. aussi, vous pouvez utiliser un attribut de données ou quelque chose sur un autre type d'élément si nécessaire
aw04

106

Vous pouvez utiliser la .bindméthode pour préconstruire les paramètres de la handleChangeméthode. Ce serait quelque chose comme:

  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'));

(J'ai également fait totalêtre calculé au moment du rendu, car c'est la chose recommandée à faire.)


13
Juste un petit rappel qui this.handleChange.bind(...)crée une nouvelle fonction. Si vous utilisez shouldComponentUpdateavec PureRenderMixinou quelque chose de similaire, il se cassera. Tous vos composants d'entrée seront renvoyés lorsqu'une seule valeur change.
zemirco

5
Si vous modifiez l'implémentation de handleChangeà, handleChange(name) { return event => this.setState({name: event.target.value}); }vous pouvez passer la "même" fonction (ne créera pas de nouvelle fonction comme c'est fait en utilisant .bind()Then, vous pouvez passer une telle fonction:this.handleChange("input1")
Andreyco

1
Je pense qu'il est également important de mentionner que la nature de setState est que l'on ne pourra pas récupérer l'état actuel dans la fonction handChange, mais l'état précédent en utilisant, this.state.input1, par exemple. Une approche appropriée sera de créer un callback tel que la this.setState(change, function () { console.log(this.state.input1); );référence: stackoverflow.com/questions/30782948/...
Charlie-Greenman

39

L' onChangeévénement bouillonne ... Vous pouvez donc faire quelque chose comme ceci:

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

Et votre méthode setField peut ressembler à ceci (en supposant que vous utilisez ES2015 ou une version ultérieure:

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

J'utilise quelque chose de similaire dans plusieurs applications, et c'est assez pratique.


11

Solution obsolète

valueLink/checkedLinksont obsolètes depuis le cœur de React, car cela déroute certains utilisateurs. Cette réponse ne fonctionnera pas si vous utilisez une version récente de React. Mais si vous l'aimez, vous pouvez facilement l'émuler en créant votre propre Inputcomposant

Ancien contenu de la réponse:

Ce que vous voulez réaliser peut être réalisé beaucoup plus facilement en utilisant les assistants de liaison de données bidirectionnels de 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 non?

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

Vous pouvez même implémenter votre propre mixin


Pourquoi devons-nous lier un onchange pour définir l'état. C'est ReactJS après tout!
Junaid Qadir le

Notez que cette solution est obsolète
Philipp

6

Vous pouvez également le faire comme ceci:

...
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>
    )
}

n'a pas fonctionné pour moi. vu les outils de développement React. sa création et son attribution de valeurs à la variable / objet appelé entrée, au lieu de input1 /
input2

1
@Gaurravs oui vous avez raison. J'avais besoin d'ajouter []autour de inputsetState. Cela rend la propriété attribuée dynamiquement
inostia

4

Vous pouvez utiliser un Reactattribut spécial appelé refpuis faire correspondre les nœuds DOM réels dans l' onChangeévénement en utilisant Reactla getDOMNode()fonction de:

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>
  ...
}

Au moins en 0.13, this.refs n'a pas de propriété pref.
blub

2
@usagidon - prevest juste un nom que j'ai défini dans la render()méthode
Janusz Kacalak

2
À partir de React v0.14, vous pouvez simplement le faire event.target === this.refs.prev. facebook.github.io/react/blog/2015/10/07/…
XåpplI'-I0llwlg'I -

0

@Vigril Disgr4ce

Lorsqu'il s'agit de formulaires multi-champs, il est logique d'utiliser la fonctionnalité clé de React: les composants.

Dans mes projets, je crée des composants TextField, qui prennent au minimum une valeur prop, et il prend soin de gérer les comportements courants d'un champ de saisie de texte. De cette façon, vous n'avez pas à vous soucier de garder une trace des noms de champ lors de la mise à jour de l'état de la valeur.

[...]

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

Vous pouvez suivre la valeur de chaque enfant inputen créant un InputFieldcomposant distinct qui gère la valeur d'un seul input. Par exemple, le InputFieldpourrait être:

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} />)
  }
})

Désormais, la valeur de chacun inputpeut être suivie dans une instance distincte de ce InputFieldcomposant sans créer de valeurs distinctes dans l'état du parent pour surveiller chaque composant enfant.


0

Je vais fournir une solution vraiment simple au problème. Supposons que nous ayons deux entrées usernameetpassword , mais que nous souhaitons que notre poignée soit simple et générique, nous pouvons donc la réutiliser et ne pas écrire de code standard.

I.Notre formulaire:

                <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.Notre constructeur, dont nous voulons sauvegarder nos usernameet password, afin que nous puissions y accéder facilement:

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

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

III. Le descripteur intéressant et "générique" avec un seul onChangeévénement est basé sur ceci:

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

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


    event.preventDefault();
}

Laisse-moi expliquer:

1.Les un changement est détecté , l' onChange(event)on appelle

Ensuite, nous obtenons le paramètre de nom du champ et sa valeur:

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

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

Sur la base du paramètre de nom, nous obtenons notre valeur à partir de l'état dans le constructeur et la mettons à jour avec la valeur:

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

4.La clé à noter ici est que le nom du champ doit correspondre à notre paramètre dans l'état

J'espère que j'ai aidé quelqu'un d'une manière ou d'une autre :)


0

La clé de votre état doit être la même que le nom de votre champ de saisie. Ensuite, vous pouvez le faire dans la méthode handleEvent;

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

-1

Salut ont amélioré la réponse ssorallen . Vous n'avez pas besoin de lier la fonction car vous pouvez accéder à l'entrée sans elle.

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'));
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.