Hi!
Looking forward to v2! Hopefully this is a simple clarification.
Version 1 looks like it has a simpleValue property, where the selected value is returned as a string. This looks like it would be perfect for basic auto-complete use cases and integration with something like react-redux-form, where all you want is a string value. The upgrade guide says this property is removed, but there are notes or suggestions about what to do to replace it. Can some sort of note be added?
Thanks!
My "not so good" solution which I will probably remove or rewrite
export const makeSimpleSelect = (SelectComponent: React.ComponentType<*>) =>
class SimpleSelect extends React.Component<Props> {
static defaultProps = {
labelKey: 'label',
valueKey: 'value',
};
select: typeof SelectComponent | null = null;
focus: () => void;
blur: () => void;
selectRef = (select: typeof SelectComponent | null) => {
this.select = select;
if (this.select) {
this.focus = this.select.focus;
this.blur = this.select.blur;
}
};
getOptionValue(option: OptionType) {
const { valueKey } = this.props;
return option[valueKey];
}
getValue(): ValueType {
const { props: { value, isMulti } } = this;
if (!isMulti) return this.findOrCreateOption(value);
return Array.isArray(value) ? value.map(val => this.findOrCreateOption(val)) : value;
}
findOrCreateOption(value: ValueType): ValueType {
const { props: { labelKey, valueKey, options } } = this;
if (value === '' || value === undefined || value === null) return value;
const option = options ? options.find(o => this.getOptionValue(o) === value) : undefined;
return option !== undefined ? option : {
[labelKey]: value,
[valueKey]: value,
};
}
handleChange = (newValue: ?OptionType, actionMeta: *) => {
const { props: { onChange, isMulti } } = this;
if (!newValue) {
onChange(newValue, actionMeta);
} else if (isMulti) {
if (Array.isArray(newValue)) {
onChange(newValue.map(val => this.getOptionValue(val)), actionMeta);
} else {
onChange([], actionMeta);
}
} else {
onChange(this.getOptionValue(newValue), actionMeta);
}
};
render() {
const value = this.getValue();
return (
<SelectComponent
ref={this.selectRef}
{...this.props}
value={value}
onChange={this.handleChange}
/>
);
}
};
I'm doing something like:
const valueKey = (o) => o.value;
const simpleValueProps = ({ value, options, onChange, getOptionValue=valueKey, ...props}) => ({
value: value ? options.find(o => getOptionValue(o) === value): value,
onChange: v => onChange(v ? getOptionValue(v) : v),
...props
});
export default ({simpleValue, ...props}) => <Select {...(simpleValue? simpleValueProps(props) : props )} />;
But it's really naive and won't work for more complex cases
I use this approach:
import find from 'lodash.find'
class SelectField extends PureComponent {
handleChange = (value) => {
return this.props.onChange(this.props.getOptionValue(value))
}
getValue () {
return find(this.props.options, (option) => this.props.getOptionValue(option) === this.props.value)
}
render () {
const {
value,
...rest
} = this.props
return (
<Select
{...rest}
value={this.getValue()}
onChange={this.handleChange} />
)
}
}
Then you can tap into the getOptionValue, so options and value work the same.
And setting getOptionValue in defaultProps can make a sensible default.
Note from the above examples only mike1808's code handles isMulti.
I've changed my solution a little by adding memoize to findOrCreateOption which is cleared when props.options are changed. And replaced focus() and blur() with React.forwardRef.
This is my major pet peeve with version 2. Like others in this thread I've written a custom component to handle the simple case. It seems like the use case of selecting one string value from an array of string values should be dead simple to implement but it isn't. It wasn't really ideal in version 1 but version 2 seems like a step backwards.
@mike1808 I'm trying to write a similar solution, and I was wondering in what cases does react-select would pass something other than an array as the newValue when isMulti === true?
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.
<SimpleValue options={[...]} value="blue">
{props => <Select {...props} />}
</SimpleValue>
valuedefaultValuegetOptionValueisMulti where value/defaultValue is a comma delimited string@jossmac does that work for controlled inputs? Also what do you mean by "really want"? Having scalar value seems like very common use case why not just have this in tree?
A wrapper is a terrible solution. I sure as hell am not looking to add another npm dependency. Are you going to keep it up-to-date? Unlikely. I suspect no one will use your wrapper and just use their own.
Having the simple case built into the library would be a much cleaner solution.
@johnomalley thank you for perpetuating the toxic attitude of open-source consumers, everyone really appreciates it.
A wrapper is a terrible solution.
It's actually pretty elegant; consumers who desire this pattern can continue to use it, and we avoid unwanted patterns leaking into core.
I sure as hell am not looking to add another npm dependency.
That is your choice. Note that I also referred to the source and recommended people grab whatever they want for their codebase.
Are you going to keep it up-to-date? Unlikely.
Luckily, many people in the open-source community don't share your "take and give nothing back" attitude, and will offer to help maintain it 馃檪
I suspect no one will use your wrapper and just use their own.
That is totally fine, and encouraged. Perhaps they'll enjoy a learning experience like I did; first time using Parcel -- how cool is that?
Having the simple case built into the library would be a much cleaner solution.
It would be a "cleaner" solution for you, because you don't have to do anything or take any responsibility for it.
Hey its just my opinion. Another library for such a simple thing seemed a bit much. My comments were not meant to be toxic. I would submit a pull request but I鈥檓 thinking it wouldn鈥檛 be welcome and it鈥檚 not worth my time for something I can work around.
Most helpful comment
A wrapper is a terrible solution. I sure as hell am not looking to add another npm dependency. Are you going to keep it up-to-date? Unlikely. I suspect no one will use your wrapper and just use their own.
Having the simple case built into the library would be a much cleaner solution.