React-select: Cannot select option from extremely simple list (codesandbox available)

Created on 13 Nov 2018  路  7Comments  路  Source: JedWatson/react-select

Why in the world doesn't this work? https://codesandbox.io/s/qzz927y0w9

import React, { Component, Fragment } from "react";
import Select from "react-select";

export default class SingleSelect extends Component {
  state = { selectedOption: "blue" };

  render() {
    const options = ["red", "blue", "green"];
    console.log("selected option is", this.state.selectedOption);
    return (
      <Select
        options={options}
        value={this.state.selectedOption}
        onChange={selectedOption => this.setState({ selectedOption })}
        getOptionLabel={option => option}
        getOptionValue={option => option}
      />
    );
  }
}
issubug-unconfirmed issureviewed

Most helpful comment

One more annoyance with this "you must convert options to objects" is that if you're using a type system, you have to routinely cast between the "option-version" and the "normal-version" of your objects. Eg, If I have my colors declared as an enum, I have to also create a "color-option" type, store ColorEnums in the component state, convert them to ColorOption in render, then cast them inside my onChange handler and my convertColorToOption function. It ends up being quite a bit of code where the end result is simply... a dropdown list.

Building a simple dropdown list of strings seems like a very common use case to me, and imo the library should address this in a more convenient manner.

I'd love to see something like this Just Work:

return (
      <Select
        options={options}
        value={this.state.selectedOption}
        onChange={selectedOption => this.setState({ selectedOption })}
      />
);

All 7 comments

@sarink

This does not work because of the function cleanValue from utils.js. It expects the value to be an object.

You could use a wrapper function around your values to convert them to objects and update state with the value of the object.

const arrToOption = opt => ({ value: opt, label: opt });

export default class SingleSelect extends Component {
  state = { selectedOption: "blue" };

  render() {
    const options = ["red", "blue", "green"];

    return (
      <Select
        options={options.map(arrToOption)}
        value={arrToOption(this.state.selectedOption)}
        onChange={selectedOption =>
          this.setState({ selectedOption: selectedOption.value })
        }
      />
    );
  }
}

CodeSandbox

Unfortunate, because it either requires a find to select the right option, or a map (like you've shown) which would cause constant unnecessary re-renders due to a new array reference every render cycle.

Also, I would've thought the purpose of the getOptionValue prop was to do this sort of comparing, which is a little misleading.

In the docs it says, getOptionValue resolves the option to a string for comparison and value attributes. It麓s, like the name of the prop suggests, just a getter function.

But the re-rendering is a valid point.

As the cleanValue function is a hindrance for this case, you have to transform your state to an object, or else it will not be treated as a valid value.
You can pass your options as an array of strings to the options prop, and use the appropriate getter functions to get value and label, but you still have to account for the object being passed to the value prop in the getter functions.

It would look like this:

<Select
    options={options}
    value={arrToOption(this.state.selectedOption)}
    onChange={selectedOption => {
        this.setState({ selectedOption: selectedOption });
    }}
    getOptionLabel={opt => typeof opt === "object" ? opt.label : opt}
    getOptionValue={opt => typeof opt === "object" ? opt.value : opt}
/>

CodeSandbox

One more annoyance with this "you must convert options to objects" is that if you're using a type system, you have to routinely cast between the "option-version" and the "normal-version" of your objects. Eg, If I have my colors declared as an enum, I have to also create a "color-option" type, store ColorEnums in the component state, convert them to ColorOption in render, then cast them inside my onChange handler and my convertColorToOption function. It ends up being quite a bit of code where the end result is simply... a dropdown list.

Building a simple dropdown list of strings seems like a very common use case to me, and imo the library should address this in a more convenient manner.

I'd love to see something like this Just Work:

return (
      <Select
        options={options}
        value={this.state.selectedOption}
        onChange={selectedOption => this.setState({ selectedOption })}
      />
);

What is the reason to accept only objects as options?

This does not work either... I would expect to select 10 km option...

<Select options={[ {label: '10 km', value: 10}, {label: '20 km', value: 20}, {label: '50 km', value: 50}, {label: '100 km', value: 100}, ]} onChange={this.props.radiusChange} getOptionValue={option => option.value} value={10} />

It only works with:

value={{label: '10 km', value: 10}}

Or when I only use label:
value={{label: '10 km'}}

Selecting only by value does not work either:
value={{value: 10}}

If I convert all values to strings it does not work either... Too bad. I guess most people here don't use it as a controlled component...

I understand the frustration and confusion. I have created a discussion thread to identify pain points and perhaps make it a bit more clear. I look forward to any contributions to the discussion to get feedback from the community and people who use react-select or make it easier for people just picking it up.
https://github.com/JedWatson/react-select/discussions/4312

Moving forward there may be some ideas to improve and simplify, but as it is for now in version 3, this is the nature of react-select, and I will be closing this issue. Not because there aren't valid points (albeit a somewhat condescending title edit), but because we want to focus on moving discussions to the discussions section and make it easier for everyone involved in the project to identify existing bugs/issues.

Was this page helpful?
0 / 5 - 0 ratings