React-select: [V2] Default value must be object

Created on 23 Jul 2018  路  16Comments  路  Source: JedWatson/react-select

Previously in V1, I could set the value as a string. The Select would use that value, match it against the option array, and default the selection. In V2, the default value must be a copy of the option it is matching against.
Example: https://codesandbox.io/s/y2x614o9nz

As the matching value I am retrieving is typically an id, I would now have to search the options, make an extract of the object that contains the matching value, and use it to initialize the state. It was much easier to just manage the value alone. Trying to initialize the Select with a simple {value: '1'} without a label doesn't work as it expects the label.

Perhaps I am missing a change or a new prop, but this was a surprise when trying to update.

categorquestion

Most helpful comment

For folks using react-final-form or similar form management libraries, there is a cleaner workaround by using the provided parse and format functions to auto-cast.

// Example by using react-final-form
// No need to cast between value and option manually
// I have not tested for multi value or other use cases, but this should be quite general
import { Field } from 'react-final-form';
import Select from 'react-select';
import { find } from 'lodash';

const SelectInput = ({ name, options }) => (
  <Field 
    name={name}
    parse={val => val && val.value}
    format={val => find(options, o => o.value === val)}
    render={({ input, meta }) => (
      <Select 
        {...input}
        options={options}
      />
    )}
)

All 16 comments

Also when you choose an option the value becomes an object like a { value: 'a', label: 'A' } not just a string 'a'. That's really bad. I need to rewrite logic for all Select components.

Yes, feels really weird to reconstruct an entry from options list in the value field.

In v1 there was a prop normalize which can be used like normalize={option => option.value}, I believe this is removed from v2.
I don't think getOptionValue and isOptionSelected can fulfill that purpose.

While this is indeed inconvenient it can be handled by writing a wrapper component that extracts the selected option from the options array. Example:
https://codesandbox.io/s/zx29v5488l
This way you don't have to change your application and can recreate the API you are used to.

@cbergmiller your wrapper component doesn't account for the use of keys besides label and value. Is there any way to extend that component to work with getOptionLabel() and getOptionValue() functions?

For folks using react-final-form or similar form management libraries, there is a cleaner workaround by using the provided parse and format functions to auto-cast.

// Example by using react-final-form
// No need to cast between value and option manually
// I have not tested for multi value or other use cases, but this should be quite general
import { Field } from 'react-final-form';
import Select from 'react-select';
import { find } from 'lodash';

const SelectInput = ({ name, options }) => (
  <Field 
    name={name}
    parse={val => val && val.value}
    format={val => find(options, o => o.value === val)}
    render={({ input, meta }) => (
      <Select 
        {...input}
        options={options}
      />
    )}
)

thanks @shawnxusy. It actually works the same for redux-form.
For multi I had to use something like this (also with lodash):

parse={val => val && map(val, item => item.value)}
format={val => map(val, item => find(options, o => o.value === item))}

the drawback is you must also access options list in the form.

Preference in favour of value being a string. Very frustrating.

This is my workaround for time being...

<Select value={ options.find(o => o.value === this.state.selectedOption) } options={options} onChange={...} />

That's big, and that's exactly why one should not use vendor libraries directly and instead wrap them so that breaking changes like this one become less painful.

import MyAmazingSelectComponent from 'somewhere';

<MyAmazingSelectComponent
  beautifulProp={1}
  value={input.value}
  onChange={callbackJustLikeBefore}
/>

This keeps coming up. Part of the problem is documentation, for sure, but if you still really want to use simple value I've created a wrapper. You can get it from npm or take a look at the source and grab whatever you want.

Usage

<SimpleValue options={[...]} value="blue">
  {props => <Select {...props} />}
</SimpleValue>

Supports

  • simple value
  • simple defaultValue
  • grouped options
  • custom getOptionValue
  • isMulti where value/defaultValue is a comma delimited string

Trivial Alternative

const getValue = (opts, val) => opts.find(o => o.value === val);

const MySelect = ({ value, ...props }) => (
  <ReactSelect value={getValue(props.options, value)} {...props} />
);

This is a list of breaking changes, shouldn't they be documented in History.md or Releases at least?

I saw the only 2 breaking changes that were documented, thought upgrading would be a breeze, but now I'm 2 hours in and ready to downgrade :)

Preference in favour of value being a string. Very frustrating.

This is my workaround for time being...

<Select value={ options.find(o => o.value === this.state.selectedOption) } options={options} onChange={...} />

This doesn't let you to choose a new item in the list

Here is solution for select with headers

<Select options={options} value={options.map(group => { return (group.options.find(o => { return (o.value === field.value)})) })} onChange={(e) => {this.props.selectChangeValue(e, field.name)}}/>

but is taking a lot of browser resources.
Any idea when we will get value not whole object with value and label?

So, just to be clear, there is no longer a valueKey prop that will let us automatically map the value string to an attribute in the options array, correct?

v1

Screen Shot 2019-07-03 at 10 34 32 AM

Screen Shot 2019-07-03 at 10 34 45 AM

Although I'm almost done with the v1 to v3 upgrade, I'm a bit worried about the memory/performance implications of this, as we have hundreds of selects around our site, many of which can theoretically grow to hundreds of options with deeply dehydrated foreign keys (and many shared components that pass the id values down to Django querymethods on our backend).

This can be done with isOptionSelected, and a wrapper component. You don't have to set the value manually.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

AchinthaReemal picture AchinthaReemal  路  3Comments

juliensnz picture juliensnz  路  3Comments

steida picture steida  路  3Comments

sampatbadhe picture sampatbadhe  路  3Comments

pgoldweic picture pgoldweic  路  3Comments