Recuperando valor de <select> con opción múltiple en React

81

La forma de React para establecer qué opción se selecciona para un cuadro de selección, es establecer un valueaccesorio especial en <select>sí mismo, correspondiente al valueatributo en el <option>elemento que desea seleccionar. Para multipleseleccionar, este accesorio puede aceptar una matriz en su lugar.

Ahora que este es un atributo especial, me pregunto cuál es la forma canónica de recuperar las opciones seleccionadas en la misma estructura de matriz de valores de opción cuando el usuario cambia las cosas (para que pueda pasarlo a través de una devolución de llamada a un componente principal, etc.), ya que presumiblemente la misma valuepropiedad no estará disponible en el elemento DOM.

Para usar un ejemplo, con un campo de texto, haría algo como esto (JSX):

var TextComponent = React.createClass({
  handleChange: function(e) {
    var newText = e.target.value;
    this.props.someCallbackFromParent(newText);
  },
  render: function() {
    return <input type="text" value={this.props.someText} onChange={this.handleChange} />;
  }
});

¿Cuál es el equivalente a reemplazar ???para este componente de selección múltiple?

var MultiSelectComponent = React.createClass({
  handleChange: function(e) {
    var newArrayOfSelectedOptionValues = ???;
    this.props.someCallbackFromParent(newArrayOfSelectedOptionValues);
  },
  render: function() {
    return (
      <select multiple={true} value={this.props.arrayOfOptionValues} onChange={this.handleChange}>
        <option value={1}>First option</option>
        <option value={2}>Second option</option>
        <option value={3}>Third option</option>
      </select>
    );
  }
});
Indicio
fuente

Respuestas:

22

Con Array.from()y e.target.selectedOptionspuede realizar una selección múltiple controlada:

handleChange = (e) => {
  let value = Array.from(e.target.selectedOptions, option => option.value);
  this.setState({values: value});
}

target.selectedOptions devuelve una colección HTMLCollection

https://codepen.io/papawa/pen/XExeZY

Mendes
fuente
¿Alguna idea sobre cómo combinar las opciones seleccionadas de varios selects en una matriz? ¿Diga después de agarrarlos a todos vía document.querySelectorAll('select')?
Casimir
Tenga en cuenta que en el momento en que se hizo esta pregunta, selectedOptionsno tenía una buena compatibilidad y aún no es compatible con IE. Sin embargo, esta sería la forma moderna de hacerlo.
Inkling
107

De la misma manera que lo hace en cualquier otro lugar, ya que está trabajando con el nodo DOM real como el objetivo del evento de cambio:

handleChange: function(e) {
  var options = e.target.options;
  var value = [];
  for (var i = 0, l = options.length; i < l; i++) {
    if (options[i].selected) {
      value.push(options[i].value);
    }
  }
  this.props.someCallback(value);
}
Jonny Buchanan
fuente
4
Versión de CoffeeScript:(option.value for option in e.target.options when option.selected)
1j01
64
Versión ES6: [... event.target.options] .filter (o => o.selected) .map (o => o.value)
svachalek
4
También puede desestructurar las flechas ES6:[...e.target.options].filter(({selected}) => selected).map(({value}) => value)
Mathieu M-Gosselin
6
RE: versión ES6, aunque se ve bien, no funciona. event.target.optionses un HTMLOptionsCollection, no un Array.
TrueWill
3
@zrisher Negative, Ghost Rider. HTMLOptionsCollectionno es iterable. Sin embargo, Array.fromtodavía funciona con él: Array.from(event.target.options).filter(o => o.selected).map(o => o.value)
Bondolin
14

La manera más fácil...

handleChange(evt) {
  this.setState({multiValue: [...evt.target.selectedOptions].map(o => o.value)}); 
}
jamesmfriedman
fuente
Esto es bueno, pero HTMLCollectionOf<HTMLOptionElement>no es una matriz ... Array.fromAunque aparentemente funciona. stackoverflow.com/a/49684109/29182
Ziggy
11

En caso de que desee utilizarlo ref, puede obtener valores seleccionados como este:

var select = React.findDOMNode(this.refs.selectRef); 
var values = [].filter.call(select.options, function (o) {
      return o.selected;
    }).map(function (o) {
      return o.value;
    });

Actualización de 2018 ES6

  let select = this.refs.selectRef;
  let values = [].filter.call(select.options, o => o.selected).map(o => o.value);
Max Podriezov
fuente
1
selectedOptions
usaré
Aquí hay una forma es6 mucho más limpia de hacerlo :) [] .filter.call (this.refs.selectRef.options, o => o.selected) .map (o => o.value);
Dg Jacquard
7

En el caso de que desee realizar un seguimiento de las opciones seleccionadas mientras se completa el formulario:

handleChange(e) {
    // assuming you initialized the default state to hold selected values
    this.setState({
        selected:[].slice.call(e.target.selectedOptions).map(o => {
            return o.value;
        });
    });
}

selectedOptionses una colección / lista similar a una matriz de elementos relacionados con el DOM. Obtiene acceso a él en el objeto de destino del evento al seleccionar valores de opción. Array.prototype.slicey callnos permite crear una copia para el nuevo estado. También puede acceder a los valores de esta manera usando una referencia (en caso de que no esté capturando el evento), que era otra respuesta para la pregunta.

rambossa
fuente
2
Como mencioné en uno de mis otros comentarios, la compatibilidad del navegador selectedOptionsparece bastante incompleta. Pero probablemente sería la solución ideal si hubiera soporte.
Inkling
1
Ah, es cierto, parece que IE todavía no es compatible con esto
rambossa
6

De hecho, puede encontrar el selectedOptionsinterior del objetivo ... no es necesario repetir todas las opciones. Imaginemos que desea enviar los valores a una onChangefunción pasada a su componente como accesorios: puede usar la siguiente función en onChangesu selección múltiple.

onSelectChange = (e) => {
    const values = [...e.target.selectedOptions].map(opt => opt.value);
    this.props.onChange(values);
  };
Lanci
fuente
En el momento en que se hizo esta pregunta, selectedOptionsno tenía una buena compatibilidad. Todavía no es compatible con Internet Explorer, por lo que no es realmente utilizable si desea compatibilidad con IE (al menos sin polyfill).
Inkling
3

Lo siguiente funcionó para mí

var selectBoxObj = React.findDOMNode(this.refs.selectBox)
var values = $(selectBoxObj).val()
mantish
fuente
1
Correcto, utilicé JQuery para recuperar los valores múltiples.
mantish
No necesitas reaccionar dom o jQuery. Simplemente recupere el valor del evento
onChange
1

Otra forma de hacerlo:

handleChange({ target }) {
    this.props.someCallback(
       Array.prototype.slice.call(target.selectedOptions).map(o => o.value)
    )
}
Aral Roca
fuente
0

Prueba este:

dropdownChanged = (event) => {
    let obj = JSON.parse(event.target.value);
    this.setState(
        {
            key: obj.key,
            selectedName: obj.name,
            type: obj.type
        });
}


<select onChange={this.dropdownChanged} >
<option value={JSON.stringify({ name: 'name', key: 'key', type: "ALL" })} >All Projetcs and Spaces</option></select>
Slavi Vatahov
fuente
Agregue más explicación a sus soluciones.
Manmohan_singh