Frilox
Frilox

Reputation: 855

React selecting option with object as attribute value

I have an issue with react when I want to change the selected option. The problem is that the value is an object and I can't pass it in option value attribut.

See the following code:

class Selector extends React.Component {
  contructor(props) {
    super(props)
    this.state = { obj: null }
    this.handleChange = this.handleChange.bind(this)
  }

  handleChange(e) {
    this.setState({obj: e.target.value})
  }

  render() {
    <select onChange={handleChange}>
     {this.props.listOption.map((option, index) => 
       <option key={index} value={option.obj}>
         {option.name}
       </option>
     )}
    </select>
  }
}

and with

<Selector option={[{name: "name", obj:{...}}, ...]}>

I need to change the state of the component with the value of the selected option. What I get when the state change is "object Object". I suppose this happens because react can't embed javascript object in attribut of final view. I am right?

Moreover, I set obj in state as null in the constructor Is there a right way to do it?

Upvotes: 41

Views: 89273

Answers (5)

Walid
Walid

Reputation: 1282

In my case, I also needed an option object (currentValue) to be selected at init.

I do not want to search for that object in this.props.listOption to get its index.

So, instead of replacing the objects with their index, I added a custom attribute data-index to the options.

The value of attribute data-index of an option option can be accessed using option.dataset.index:

handleChange(e) {
  const selectedIndex = e.target.selectedOptions[0].dataset.index];
  this.setState({obj: this.props.listOption[selectedIndex].obj})
}

render() {
  <select value={currentValue} onChange={handleChange}>
    {this.props.listOption.map((option, index) =>
      <option key={index} value={option.obj} data-index={index}>
        {option.name}
      </option>
    )}
  </select>
}

That code should not be difficult to adapt to multiple selects.

Upvotes: 0

Codemaker2015
Codemaker2015

Reputation: 1

Try this following code,

import React from 'react';

class LocationDemo extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            searchLoc: undefined,
            selectedLoc: "",
            locs:[
                {"name" : "Kerala","districts":["Ernakulam", "Trivandrum"]},
                {"name" :"Tamil Nadu","districts" :["Palani","Tiruchi"]}
            ],
        };
        this.handleChangeLocation = this.handleChangeLocation.bind(this);
    }


    handleChangeLocation = (event) => {
        this.setState({ selectedLoc: event, searchLoc: event.target.value }
            , () => console.log("searchLoc", this.state.searchLoc));
    }

    render() {
        return (
            <select class="cls-location" id="id-location"
                onChange={this.handleChangeLocation}
                value={this.state.locs.find(obj => obj.value === this.state.selectedLoc)}
                required
            >
                {
                    this.state.locs.map(loc => {
                        return (
                            <option name={loc.name} value={JSON.stringify(loc)}>{loc.name}</option>
                        )
                    })
                }
            </select>

        );
    }
}

export default LocationDemo;

Upvotes: 0

Sreeragh A R
Sreeragh A R

Reputation: 3021

Convert the object to JSON string, and pass it as value.

And convert the string back to object in the handler.

handleChange(event) {
    let obj = JSON.parse(event.target.value); //object
  }

  render() {
    <select onChange={handleChange}>
     {this.props.listOption.map((option, index) => 
       <option key={index} 
         value={JSON.stringify(option)}> //pass object string as value
         {option.name}
       </option>
     )}
    </select>
  }

Upvotes: 38

Jyothi Babu Araja
Jyothi Babu Araja

Reputation: 10282

You can make use of index of options

class Selector extends React.Component {
  contructor(props) {
    super(props);
    this.state = { obj: null }
    this.handleChange = this.handleChange.bind(this)
  }

  handleChange(e) {
    this.setState({obj: this.props.listOption[e.target.value].obj})
  }

  render() {
    <select onChange={handleChange}>
     {this.props.listOption.map((option, index) =>
       <option key={index} value={index}>
        {option.name}
       </option>
      )}
    </select>
  }
}

Moreover, I set obj in state as null in the constructor Is there a right way to do it?

I depends on your requirement. If you want to show at least one option as selected you can keep that instead of null

Upvotes: 64

Luke359
Luke359

Reputation: 457

I assume you want only one option will be selected. So the easiest way would be to set selectedIndex. When using construct always think of value type. this.state = { selectedIndex: 0}

Now you've state with selectedIndex object which firstly is equal to 0.

In render method you could then just check for the index:

{this.props.listOption.map((option, index) => {
    this.state.selectedIndex == index ? (
      <option key={index} value={option.obj} selected>option.name</option>
    ): (
      <option key={index} value={option.obj}>option.name</option>
    ))}

And on handle change setState with e.target.key.

I may have left syntax errors... Altought I hope it helps.

Upvotes: 3

Related Questions