2

In a react app, you should control a <select>. It's real easy with a single select:

const ExampleSelect = () => {
  const [value, setValue] = React.useState()
  return (
    <select
      value={value}
      onChange={(e) => setValue(e.currentTarget.value)}
    >
      <option value='foo'>Foo</option>
      <option value='bar'>Bar</option>
      <option value='baz'>Baz</option>
    </select>
  )
}

But when you add the multiple prop to a <select> things get weird. Functions passed to onChange that iterate through the options and take actions based on which ones have option.selected === true only seem to work on desktop -- in Safari on iOS, a select with multiple === true seems impossible to control.

I feel like this is a really basic question, but I can't find anything on it. I'm not looking to install a whole library that comes with pre-styled components just to be able to select multiple options in a <select> inside a react app.

Here are a few onChange functions I've tried:

type Setter = React.Dispatch<React.SetStateAction<string[] | undefined>>
type SelectChange = React.ChangeEvent<HTMLSelectElement>

export const handleMultiSelectChange = <S extends Function = Setter>(
  value: string[] | undefined, setter: S
) => (
    e: SelectChange
  ): void => {
    const v = e.currentTarget.value
    if (value && value.length > 0) {
      const index = value.indexOf(v)
      if (index >= 0) {
        const newValue = value.filter((opt) => opt !== v)
        setter(newValue)
      } else {
        setter([...value, v])
      }
    } else {
      setter([v])
    }
  }
type Setter = React.Dispatch<React.SetStateAction<string[] | undefined>>
type SelectChange = React.ChangeEvent<HTMLSelectElement>

export const handleChangeMultiple = <S extends Function = Setter>(
  value: string[] | undefined = [], setter: S
) => (
    event: SelectChange
  ): void => {
    const {options} = event.currentTarget
    for (let i = 0, l = options.length; i < l; i += 1) {
      const option = options[i]
      if (option.selected) {
        alert(value.includes(option.value))
        if (value.includes(option.value)) {
          const index = value.indexOf(option.value)
          value.splice(index, 1)
        } else {
          value.push(option.value)
        }
      }
    }
    setter(value)
  }

(those ones are used like this:)

const ExampleSelect = () => {
  const [value, setValue] = React.useState()
  return (
    <select
      multiple
      value={value}
      onChange={handleChangeMultiple(value, setValue)}
    >
      <option value='foo'>Foo</option>
      <option value='bar'>Bar</option>
      <option value='baz'>Baz</option>
    </select>
  )
}

(though that's not working correctly for me)

1 Answer 1

6

For select, e.target gives you options array and you can loop thru it getselected option. Use map to grab the actual values and store them in your state.

select

   <select
        onChange={handleChangeNormalSelect}
        multiple
        value={val}
        options={options}
      >
        {options.map(item => {
          return <option value={item.value}>{item.label}</option>;
        })}
      </select>

onChange

...
const [val, setVal] = useState([]);
...
const handleChangeNormalSelect = e => {
    const updatedOptions = [...e.target.options]
      .filter(option => option.selected)
      .map(x => x.value);
    console.log("updatedOptions", updatedOptions);
    setVal(updatedOptions);
  };
...

Working code sample(html multi select) is here

Sign up to request clarification or add additional context in comments.

4 Comments

This code sample doesn't help... you're using the react-select library. I'm explicitly trying to use a plain <select> so I don't have to install a bunch of libraries that come with opinionated styling.
The html multi select also does not work. You can only select a single option in it at a time.
it works... hold control button (or command button if using mac) and click thru options to select multiple.... also i have removed react-select part of code from the codesandbox..
dang, sorry for that @gdh... when using a plain html select with multiple === true outside of a react app, you don't need to hold cmd/shift to select multiple on a Mac. I clicked one option, it became selected, then I clicked another, and then the second became the only selected option. I see now that you're right, using cmd/shift does allow me to select multiple or a range of the presented options. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.