'React: OnChange for drop down return me the entire <select></select> tag

I am working with react and I have a dropdown menu which is generated like that:

Select a user : <select defaultValue={'DEFAULT'} onChange={this.handleFilter}><option value="DEFAULT" disabled>-- select an gangster --</option>{ddUsers}</select>
            

ddUsers:

let ddUsers = this.state.users.map(user => <option key={uuid.v4()} value={user}>{user.name}</option>)
    

And here is my users array:

users : [
    { name: 'All',    key : uuid.v4()}, 
    { name: 'Koko',   key : uuid.v4()},
    { name: 'Krikri', key : uuid.v4()},
    { name: 'Kéké',   key : uuid.v4()}
  ]

My problem is that when I want to use the event.target it return me the entire select tag (here the output of console.log(event.target):

<select>
​<option value=​"DEFAULT" disabled selected>​-- select an gangster --​</option>
​<option value=​"[object Object]​">​All​</option>​
<option value=​"[object Object]​">​Koko​</option>​ 
<option value=​"[object Object]​">​Krikri​</option>​
<option value=​"[object Object]​">​Kéké​</option>​
</select>​

where it should normally return me only the user. here is my handleUser (which displays me select tag above):

handleFilter = (event) => {
    console.log(event.target);
} 

I am lost on what I am missing. I have something similar and it's working perfectly.



Solution 1:[1]

What you want is a key/value at select value, but unfortunately, it does not work. Maybe you'll need to use another component like React-select or use JSON.stringfy() and JSON.parse()

I have made an abstraction code using JSON methods.

const uuid = {
  v4() {
    return Math.random();
  }
};

const users = [
  { name: "All", key: uuid.v4() },
  { name: "Koko", key: uuid.v4() },
  { name: "Krikri", key: uuid.v4() },
  { name: "Kéké", key: uuid.v4() }
];

function App() {
  const [selected, setSelected] = React.useState("");

  function parseSelected(event) {
    const valueToParse = event.target.value;
    const itemSelected = JSON.parse(valueToParse);
    setSelected(itemSelected);
    return;
  }

  return (
    <div>
      <select name="any" id="any" onChange={parseSelected}>
        {users.map(user => (
          <option key={user.key} value={JSON.stringify(user)}>
            {user.name}
          </option>
        ))}
      </select>
      <p>Selected name: {selected.name}</p>
      <p>Selected key: {selected.key}</p>
    </div>
  );
}

the reason is Option HTML interface accepts only DOMString as a value. You can see the docs here

Solution 2:[2]

You could do something like this.. This allows for flexibility so you can use this <select> component anywhere, using any object with any property.. (using @CarlosQuerioz answer, you'd always have to use an object with a name key)

Parameter meanings:

  • data: array you are wanting to use as options for select
  • value: the key from your data property that you want to display
  • placeholder: placeholder value (not selectable)
  • defaultOption: default selected option (more on this below)
  • onSelectChange: event that is fired when selection changes

How we handle defaultOption:

  • If you pass in an array of objects to defaultOption we will select the first object in that array, and use the specified value key to determine the value to be shown
  • If you pass in an object, we will show the specified value key

To demonstrate the defaultOption, uncomment each of these values in the example below, you'll see what I mean.

    const users = [                 // If this was your `data` object
      { name: "Karkar", key: 1 },
      { name: "Koko", key: 2 },
      { name: "Krikri", key: 3 },
      { name: "Kéké", key: 4 }
    ];

      <MySelect
        data={users}
        value="name"                // Must be a valid key that your objects have
        onSelectChange={handle}
        defaultOption={users[0].name} 
        //defaultOption={users[1]}  // Would display 'Koko' by default
        //defaultOption={users}     // Would display 'Karkar' by default
      />

EXAMPLE:

const { useState, useEffect } = React;
const { render } = ReactDOM;

function MySelect({ data, value, defaultOption = "", onSelectChange, placeholder = "Select an option" }) {
  const [selected, setSelected] = useState(defaultOption);
  
  const handleOnSelectChange = selection => {
    onSelectChange && onSelectChange(selection);
  }

  const handleChange = event => {
    let sel = data.find(d => d[value] === event.target.value);
    setSelected(sel);
    handleOnSelectChange(sel);
  };

  useEffect(() => {
    if (typeof selected === "object") {
      let val = selected.length > 0 ? selected[0] : selected;
      setSelected(val);
      handleOnSelectChange(val);
    }
  }, []);

  return (
    <select value={selected[value]} onChange={handleChange}>
      <option value="" disabled>
        {placeholder}
      </option>
      {data && data.map((itm, indx) => (
        <option key={indx} value={itm[value]}>
          {itm[value]}
        </option>
      ))}
    </select>
  );
}

const users = [
  { name: "Karkar", key: 1 },
  { name: "Koko", key: 2 },
  { name: "Krikri", key: 3 },
  { name: "Kéké", key: 4 }
];

function App() {
  const [user, setUser] = useState();

  const handle = selectedUser => {
    setUser(selectedUser);
  };
  
  return (
    <div>
      <MySelect
        data={users}
        value="name"
        placeholder="Please make a selection"
        onSelectChange={handle}
        //defaultOption={users[0].name}
        //defaultOption={users[0]}
        defaultOption={users}
      />
      {user && <pre>{JSON.stringify(user, null, 2)}</pre>}
    </div>
  );
}

render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2