InsightsTopicsContact
Eckher
Your guide to what's next.
Eckher
Insights
Topics
Home › Eckher Insights › 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.

Cover
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?
Asynchronous iteration in JavaScript
Gotchas with asynchronous iteration in JavaScript.
Creating a playlist in React
How to create a playlist in React?
Flow-relative padding and margin
Why is padding-inline-start better than padding-left?
Eckher
Your guide to what's next.
Copyright © 2021 Eckher. Various trademarks held by their respective owners.