React.js: identificación de diferentes entradas con un controlador onChange

141

Curioso sobre cuál es la forma correcta de abordar esto:

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

Obviamente, podría crear funciones de handleChange separadas para manejar cada entrada diferente, pero eso no es muy bueno. Del mismo modo, podría crear un componente solo para una entrada individual, pero quería ver si hay una manera de hacerlo así.

T3db0t
fuente

Respuestas:

163

Sugiero apegarse a los atributos HTML estándar como nameen inputElements para identificar sus entradas. Además, no necesita mantener "total" como un valor separado en el estado porque es componible agregando otros valores en su estado:

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
fuente
3
Estaba tratando de hacer esto inicialmente, pero setState ({e.target.name ... no funciona, es un error de sintaxis. Debe hacer un objeto temporal con obj [e.target.name] y setState para eso. Este tipo de trabajo funciona, pero parece haber algún tipo de retraso en la actualización del objeto de estado.
T3db0t
1
El "tipo de retraso" se establece Actualización de estado durante requestAnimationFrame (supongo), así que ignore ese comentario
T3db0t
24
@ T3db0t Esta sintaxis de ES6 funciona:this.setState({ [e.target.name]: e.target.value });
XåpplI'-I0llwlg'I -
2
Usar bind es el mejor enfoque. Este enfoque no funciona si adjunta el controlador onChange a un elemento sin una propiedad de nombre, como un div.
ericgrosse
3
@ericgrosse bind no es mejor en este caso, ya que estás creando muchas funciones innecesarias. Además, puede usar un atributo de datos o algo en un tipo diferente de elemento si es necesario
aw04
106

Puede usar el .bindmétodo para precompilar los parámetros del handleChangemétodo. Sería algo así como:

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

(También hice que totalse calcule en el momento del renderizado, ya que es lo más recomendable).

fiatjaf
fuente
13
Solo un recordatorio rápido que this.handleChange.bind(...)crea una nueva función. En caso de que esté usando shouldComponentUpdatecon PureRenderMixino algo similar se romperá. Todos sus componentes de entrada se volverán a procesar cuando cambie un solo valor.
zemirco
55
Si cambia la implementación de handleChangea handleChange(name) { return event => this.setState({name: event.target.value}); }, puede pasar la "misma" función (no creará una nueva función como se hace usando .bind()Entonces, puede pasar dicha función:this.handleChange("input1")
Andreyco
1
Creo que también es importante mencionar, que la naturaleza de setState es que uno no podrá recuperar el estado actual en la función handChange, sino el estado anterior que usa, this.state.input1, por ejemplo. Un enfoque adecuado será crear una devolución de llamada como this.setState(change, function () { console.log(this.state.input1); );referencia: stackoverflow.com/questions/30782948/…
Charlie-Greenman
39

El onChangeevento burbujea ... Entonces puedes hacer algo como esto:

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

Y su método setField podría verse así (suponiendo que esté usando ES2015 o posterior:

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

Utilizo algo similar a esto en varias aplicaciones, y es bastante útil.

Christopher Davies
fuente
11

Solución obsoleta

valueLink/checkedLinkestán en desuso del núcleo React, porque confunde a algunos usuarios. Esta respuesta no funcionará si usa una versión reciente de React. Pero si te gusta, puedes emularlo fácilmente creando tu propio Inputcomponente

Viejo contenido de respuesta:

Lo que desea lograr puede lograrse mucho más fácilmente con los ayudantes de enlace de datos bidireccionales 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'));

Fácil verdad?

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

Incluso puedes implementar tu propio mixin

Sebastien Lorber
fuente
¿Por qué tenemos que vincular un cambio para establecer el estado? ¡Es ReactJS después de todo!
Junaid Qadir
Tenga en cuenta que esta solución está en desuso
Philipp
6

También puedes hacerlo así:

...
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
fuente
No funcionó para mí. vio React herramientas de desarrollador. es crear y asignar valores a la variable / objeto llamado input, en lugar de input1 / input2
Gaurravs
1
@Gaurravs sí, tienes razón. Necesitaba agregar []alrededor inputen setState. Eso hace que la propiedad sea asignada dinámicamente
inostia
4

Puede usar un Reactatributo especial llamado refy luego hacer coincidir los nodos DOM reales en el onChangeevento utilizando Reactla getDOMNode()función 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
fuente
Al menos en 0.13 this.refs no tiene una propiedad pref.
blub
2
@usagidon - preves solo un nombre que configuré en el render()método
Janusz Kacalak
2
A partir de React v0.14, puedes hacerlo event.target === this.refs.prev. facebook.github.io/react/blog/2015/10/07/…
XåpplI'-I0llwlg'I -
0

@Vigril Disgr4ce

Cuando se trata de formas de campo múltiple, tiene sentido usar la característica clave de React: componentes.

En mis proyectos, creo componentes TextField, que toman un valor de apoyo como mínimo, y se encarga de manejar los comportamientos comunes de un campo de texto de entrada. De esta forma, no tiene que preocuparse por realizar un seguimiento de los nombres de campo al actualizar el estado del valor.

[...]

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
fuente
0

Puede realizar un seguimiento del valor de cada hijo inputcreando un InputFieldcomponente separado que gestione el valor de un solo input. Por ejemplo, el InputFieldpodría ser:

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

Ahora inputse puede rastrear el valor de cada uno dentro de una instancia separada de este InputFieldcomponente sin crear valores separados en el estado primario para monitorear cada componente secundario.

Javasamurai
fuente
0

Proporcionaré una solución realmente simple al problema. Supongamos que tenemos dos entradas usernamey password, pero queremos que nuestro identificador sea fácil y genérico, para poder reutilizarlo y no escribir código repetitivo.

I. Nuestra 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.Nuestro constructor, del que queremos guardar nuestro usernamey password, para que podamos acceder a ellos fácilmente:

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

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

III. El manejo interesante y "genérico" con un solo onChangeevento se basa en esto:

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

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


    event.preventDefault();
}

Dejame explicar:

1.Cuando se detecta un cambio, onChange(event)se llama

2. Luego obtenemos el parámetro de nombre del campo y su valor:

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

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

3. De acuerdo con el parámetro de nombre, obtenemos nuestro valor del estado en el constructor y lo actualizamos con el valor:

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

4.La clave a tener en cuenta aquí es que el nombre del campo debe coincidir con nuestro parámetro en el estado

Espero haber ayudado a alguien de alguna manera :)

Georgi Peev
fuente
0

La clave de su estado debe ser la misma que el nombre de su campo de entrada. Entonces puede hacer esto en el método handleEvent;

this.setState({
        [event.target.name]: event.target.value
});
Rajan
fuente
-1

Hola, he mejorado la respuesta ssorallen . No necesita vincular la función porque puede acceder a la entrada sin ella.

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é
fuente