How to get "key" prop from React element (on change)?

JavascriptReactjs

Javascript Problem Overview


I want to get option's value and key when select onChange.

I know I can get option's value simplicity used event.target.value in onChangeOption function, but how can I get key?

<select onChange={this.onChangeOption} value={country}>
    {countryOptions.map((item, key) =>
        <option key={key} value={item.value}>
            {item.name}
        </option>
    )}
</select>

Javascript Solutions


Solution 1 - Javascript

You will need to pass value in key as a different prop.

From docs:

> Keys serve as a hint to React but they don’t get passed to your components. If you need the same value in your component, pass it explicitly as a prop with a different name:

Read: https://reactjs.org/docs/lists-and-keys.html

Solution 2 - Javascript

Passing key as a prop works obviously, but much quicker way could be to include the key as a custom attribute in your html.

class App extends React.Component {
  constructor(props) {
    super(props);
    this.onSelect = this.onSelect.bind(this);
  }
  onSelect(event) {
    const selectedIndex = event.target.options.selectedIndex;
        console.log(event.target.options[selectedIndex].getAttribute('data-key'));
  }

  render() {
    return ( 
      <div>
      <select onChange = {this.onSelect}>
       <option key="1" data-key="1">One</option> 
       <option key="2" data-key="2">Two</option>             </select> 
     </div>
    );
  }
}

ReactDOM.render( < App / > , document.getElementById('root'));

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Solution 3 - Javascript

Imagine a component like this:

<Component data={[{id:'a23fjeZ', name="foo"}, ...]}` 

Which renders a list of inputs, and gets in a data prop which is a collection (Array of Objects):

function Component ( props ){
    // cached handler per unique key. A self-invoked function with a "cache" object
    const onChange = (cache => param => {
        if( !cache[param] )
            cache[param] = e => 
                console.log(param, e.target.name, e.target.value)

        return cache[param];
    }
    )({});

    return props.data.map(item =>
        <input key={item.id} name={item.name} onChange={onChange(item.id)} />
    }

}

As you can see, the key isn't being accessed, but is passed into a currying function which is handling the caching internally, to prevent "endless" creation of functions on re-renders.

Solution 4 - Javascript

So you do need to pass the key into the <option> as a prop, but since the <select> is a native field and handles changes internally it gets a little hard to get that key value back out. Lets start one issue at a time, passing the prop into the option could be as simple as wrapping the option component like so and using the index prop as the new key prop.

const CustomOption = ({ value, children, index }) => (
    <option key={index} value={value}>{value}</option>
);

Also note that we're creating a custom component wrapper above to swallow the index prop from being applied to <option /> itself as react doesnt like unknown props on dom elements.

Now if we could handle the selected event inside the option we would be done, but we can't do that. so we need to make a custom select as well:

class CustomSelect extends React.Component {

    static propTypes = {
        value: PropTypes.object,
        onChange: PropTypes.func,
    }

    handleChanged = (e) => {
        const newValue = e.target.value;
        let newKey;

        // iterate through our children searching for the <CustomOption /> that was just selected
        React.children.forEach(this.children, (c) => {
            if (c.props && c.props.index && c.props.value === newValue) {
                newKey = c.props.key;
            }
        });

        this.props.onChange(e, newKey);
    }

    render() {
        return (
            <select value={this.props.value} onChange={this.handleChanged}>
                {this.props.children}
            </select>
        );
    }
}

it has two props value, and onChange. Notice that we intercept the change event in order to find the index (the key) and we pass it along to the parent as the second parameter. This is not very nice but I can't think of another easy way to do this while still using the native <select> element.

Note you need to replace your usages of <select> and <optoin> to use these new classes, and assign the index prop along with the key prop on the option.

Solution 5 - Javascript

You can pass anything through value. I think this solves your problem.

<select onChange={this.handleChange}>
        {this.state.countryOptions.map((item, key) =>
          <option key={key} 
            value={JSON.stringify({key, value:item.value})}>
            //passing index along with value
            {item.value}
          </option>
        )}
</select>

handleChange(e) {
    let obj = JSON.parse(e.target.value) 
   // gives object{ key:country index, value: country value}
  }

Solution 6 - Javascript

A simple way of doing this is:

const functionCall = (event) => {
    console.log(event.target.getAttribute('a-key'));
}

<button a-key={1} onClick={functionCall}>Press Me</button>

I put onClick but you can use any event.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjimmyView Question on Stackoverflow
Solution 1 - JavascriptNaisheel VerdhanView Answer on Stackoverflow
Solution 2 - JavascriptAgneyView Answer on Stackoverflow
Solution 3 - JavascriptvsyncView Answer on Stackoverflow
Solution 4 - JavascriptcaesayView Answer on Stackoverflow
Solution 5 - JavascriptSreeragh A RView Answer on Stackoverflow
Solution 6 - JavascriptValentinView Answer on Stackoverflow