12
votes

I'm trying to get the id,name, and value from Select field element of Material-UI react component.

This is my container:

//some code is removed to keep things simple
class MyContainer extends Component {

  constructor(props) {
    super(props);
  }

  render() {
    return(
        <MyComp onChange={this._onChange.bind(this)}  />
    );
  }

  _onChange(evt) {
    console.log(evt.target); 
    console.log(evt.target.id);  //blank
    console.log(evt.target.name); //undefined 
    console.log(evt.target.value); //html value of selected option!

  }

 }
 export default connect(select)(MyContainer);

in my presentational component:

  import React, {Component} from 'react';
  import Select from 'material-ui/SelectField';
  import MenuItem from 'material-ui/MenuItem';

  const someData = ["aaaa", "bbbb", "ccccc"];

  class MyComp extends Component {

    render() {
      const {onChange} = this.props;

      return (
        <form >
            <Select
                    value={this.props.test}
                    name={"test"}
                    id={"test"}
                    onChange={this.props.onChange}
                    hintText={"Select a fitch rating service"}>
              {
                someData.map((e) => {
                  return <MenuItem key={Math.random()}  value={e} primaryText={e}/>
                })
              }
            </Select>
        </form>
      );
    }
  }

Problem is that _onChange(evt) is giving this values:

  evt.id is blank
  evt.name is undefined
  evt.target.value is <div>whatever the selected value was</div> 

It seems as though the passed argument to _onChange(evt) is not the SELECT but rather the option since when i print out evt.target it gives <div>whatever the selected value was</div>. anyone knows why? If i use a plain select field (not material-ui) then this works as expected (i.e. i can get the id,name, correct value of selected option). How do i get the target id, name..etc from onChange event of Material-UI Select component?

P.S i'm using the same _onChange method for TextField component of material-ui and it works there. I've also tried:

 _onChange = (event, index, value) => {
    console.log(event.target.id); //blank
    console.log(event.target.name); //undefined
    console.log(index); //correct index
    console.log(value); //correct value
  };
3

3 Answers

4
votes

Update 2

In response to your comments:

As per the material-ui docs, getting back the touchtap event on option element rather than the select element is expected. If you want the id and name of the element, I would suggest binding the variables to the callback:

The onchange method in the parent component:

_onChange(id, name, evt, key, payload) {

  console.log(id);  //id of select
  console.log(name); //name of name
  console.log(payload); //value of selected option
}

And when you attach it to the select component, you need to use bind

   <Select
     value={this.props.test}
     name={"test"}
     id={"test"}
     onChange={this.props.onChange.bind(null,"id","name")}
     hintText={"Select a fitch rating service"}>

Update

Here are the react event docs. Under the event-pooling you will find reference to the use of e.persists() in the block quote. The explanation given in this issue is that React pools the event object, so it get's reused when another event is fired.


React has a rather special event object. It wraps the native event in a synthetic event, which is what event points to in your _onChange method. The problem is that this synthetic event object is recycled and reused for the next event, so it is garbage collected and set to null. I don't think this is well documented, but I'll search for a link and update this answer once I find it.

The solution is for the first line in your event handler to make a copy of the event, persist it, or get a reference to the native event:

Make a copy of the event

_onChange = (event, index, value) => {
  e = _.cloneDeep(event); // Here I'm using the cloneDeep method provided by underscore / lodash . User whatever library you prefer. 
  console.log(e.target.id); 
  console.log(e.target.name); 
};

Persist the event

_onChange = (event, index, value) => {
  event.persist() // This stops react from garbage collecting the event object. It may impact performance, but I doubt by much.
  console.log(event.target.id); 
  console.log(event.target.name); 
};

Get a reference to the native event object

_onChange = (event, index, value) => {
  e = event.native; // This looks the same as a vanilla JS event
  console.log(e.target.id); 
  console.log(e.target.name); 
};
6
votes

I'd keep it simpler using event.currentTarget instead of event.target. In your event handler, you can access the attribute of interest from the element that triggered the event by:

_onChange(evt) {
    console.log(evt.currentTarget.getAttribute("data-name");
    console.log(evt.currentTarget.getAttribute("data-value");
}

thus calling .getAttribute on the attribute name prepended by "data-"

1
votes

This is an answer for latest version of Material UI and React (2020):

You need to have the name property on your <Select> element. That name value should correspond to the field's name in your object.

This should work (name is set to the field you're trying to update):

 <Select
            labelId="demo-simple-select-outlined-label"
            id="demo-simple-select-outlined"
            name="taskType"
            value={props.job.taskType || ""}
            label="{props.label}"
            onChange={props.onTaskTypeChange}
          >

But this will not work (name is omitted). This will return evt.target.name = undefined:

 <Select
            labelId="demo-simple-select-outlined-label"
            id="demo-simple-select-outlined"
            value={props.job.taskType || ""}
            label="{props.label}"
            onChange={props.onTaskTypeChange}
          >