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.

T3db0t
la source

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'));
Ross Allen
la source
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.)

Fiatjaf
la source
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.

Christopher Davies
la source
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

Sébastien Lorber
la source
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>
    )
}
Inostia
la source
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>
  ...
}
Janusz Kacalak
la source
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} />;
}

[...]
manu3569
la source
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.

Javasamurai
la source
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 :)

Georgi Peev
la source
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
});
Rajan
la source
-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'));
Albert Olivé
la source