Eckher
EckherInsightsContact
Eckher
Your guide to what's next.
Multi-<select> in React
Dec 13, 2019

Multi-<select> in React

How to implement a controlled multi-<select> in React?

Unlike the regular <select>, multi-<select> has more complex onChange logic:

type Value = any

type MultiSelectProps = {
    options: {
        label: string
        value: Value
    }[]
    values: Value[]
    onChange: (values: Value[]) => void
}

const MultiSelect = ({ options, values, onChange }: MultiSelectProps) => (
    <select
        multiple={true}
        onChange={(event: React.ChangeEvent<HTMLSelectElement>) => {
            const selectedOptions = options.filter((option, optionIndex) => event.target.options[optionIndex].selected)
            onChange(selectedOptions.map(({ value }) => value))
        }}
        defaultValue={[]}
    >
        {options.map(({ label, value }) => (
            <option key={value} value={value} selected={values.includes(value)}>{label}</option>
        ))}
    </select>
)

If the Value type is string, the onChange prop passed to <select> can be simplified:

type MultiSelectProps = {
    options: {
        label: string
        value: string
    }[]
    values: string[]
    onChange: (values: string[]) => void
}

const MultiSelect = ({ options, values, onChange }: MultiSelectProps) => (
    <select
        multiple={true}
        onChange={(event: React.ChangeEvent<HTMLSelectElement>) => {
            const selectedOptions = [...event.target.selectedOptions]
            onChange(selectedOptions.map(({ value }) => value))}
        }
        defaultValue={[]}
    >
        {options.map(({ label, value }) => (
            <option key={value} value={value} selected={values.includes(value)}>{label}</option>
        ))}
    </select>
)

Note that HTMLSelectElement.selectedOptions is not supported in Internet Explorer ≤ 11. Use this line instead:

const selectedOptions = [...event.target.options].filter(({ selected }) => selected)

It still requires transpilation because it uses ES2015's const, array spread, and parameter destructuring syntax.

See also
Built-in React hooks
The list of built-in React hooks.
Declaring custom JSX/HTML attributes in TypeScript
How to specify non-standard JSX/HTML attributes in TypeScript?
<select> in React
How to implement a controlled <select> in React?
Styling native HTML dropdowns
How to style the native HTML dropdown?
Authenticating to an npm registry
How to authenticate to an npm registry?