JedWatson / react-select

The Select Component for React.js
https://react-select.com/
MIT License
27.6k stars 4.12k forks source link

Runtime error if options are array of strings #5369

Open alamothe opened 2 years ago

alamothe commented 2 years ago

If options are array of strings:

            <Select
                options={["a", "b", "c"]}
                value={value}
                getOptionValue={e => e}
                getOptionLabel={e => e}
                onChange={e => setValue(e)}
            />

There is a runtime error:

Uncaught TypeError: Cannot use 'in' operator to search for 'options' in 1

Which happens here: https://github.com/JedWatson/react-select/blob/master/packages/react-select/src/Select.tsx#L382

Sometimes it is really convenient if options is an array of predefined strings (enum).

It could be fixed just by doing a typeof guard.

JonnyHaystack commented 1 year ago

I ran into this problem as well, with an enum. What was your workaround?

fen-x commented 1 year ago

Most compact workaround I could think of is to use String object. Being an object, it's compatible with in operator.

Since String object and string primitive are both coerce to the same string value, only options has to be wrapped with objects.

<Select
  options={["a", "b", "c"].map(it => new String(it))}
  value={value}
  getOptionValue={String}
  getOptionLabel={String}
  onChange={e => setValue(String(e))}
/>

See more about String() constructor and about primitive wrapper types caveats.

fen-x commented 1 year ago

Which happens here: https://github.com/JedWatson/react-select/blob/master/packages/react-select/src/Select.tsx#L382

BTW, due to some changes the line has moved since then. Now (v5.7.0) it is L397.

alamothe commented 1 year ago

The String workaround doesn't work for me if it's not a isMutli. I wasn't able to make it work with single select

juhana commented 1 year ago

Typescript complains about the new String() wrapper workaround. This is the only way I could make it work:

options={ [{ options: ["a", "b", "c"] }] }

which fakes a GroupBase type. The downside is that there's a small extra margin at the start of the dropdown but it can be fixed with CSS.

felixfbecker commented 6 months ago

Another option is to map all the strings into a wrapper object like

options.map(o => ({ value: o })

however you have to then also map value properly:

value={options.find(o => o.value === value)}

it would be much nicer if using string values worked directly, especially when migrating existing <select> usages (which only work with string values) to react-select.