AsyncCreatable is working well, until I add a custom filterOptions prop. At that point async lookup still works, however the creatable behavior disappears - there is no longer an option to add an unrecognized value.
The custom filter I'm using works as expected with both the Creatable hoc and the base Select component. The issue is only with AsyncCreatable. My filter is just removing options a user previously selected, but which react-select is no longer aware of.
The issue manifests even with a simple filter function that just returns the options unmodified. Here's a plunker to demonstrate:
https://plnkr.co/edit/KWDD9e2OexIHMiej6ZK2?p=preview
Turns out using an identity function as a filter for a standard Creatable select adds a new "Create option" option on each keystroke - maybe that points to another bug? 馃槃 While slightly annoying, the main issue described is still observable with the last AsyncCreatable - nothing can be created.
I experience the same behaviour, I have something like this:
const SelectAsync = ({ ..., creatable = false }) => {
const S = creatable ? Select.AsyncCreatable : Select.Async
return (
<S
...
filterOption={false}
/>
)
}
@oomathias Doesn't that just disable the filter function by overriding it with false? I'd like to use a custom filter for async lookup while maintaining the ability to create items.
@MilllerTime I'm just saying that even with false it's not working. I have the same issue :smiley:
@oomathias Ahh sorry, I thought you were proposing a workaround 馃槃 Nice observation! I can confirm filterOptions={false} also breaks the creatable feature. This is getting _weird_.
Well, actually I think I've spotted the source of the problem.
Creatable is overriding the filterOptions because all the logic is inside this function.
See here https://github.com/JedWatson/react-select/blob/master/src/Creatable.js#L101 and https://github.com/JedWatson/react-select/blob/master/src/AsyncCreatable.js#L23
<Select {...reduce(asyncProps, reduce(creatableProps, {}))} />
// which looks like is equivalent to
<Select {...creatableProps} {...asyncProps} />
If you pass a filterOptions to AsyncCreatable, the Select component gets asyncProps.filterOptions, if none is passed Select component gets creatableProps.filterOptions (so in this case it works)
Fix:
{...creatableProps} {...omit(asyncProps, ['filterOptions'])}
Does the work, but it's still a lot buggy.
Turns out using an identity function as a filter for a standard Creatable select adds a new "Create option" option on each keystroke
It doesn't fix that.
@bvaughn Could you have a look at this?
@oomathias:
The filterOptions method in Creatable passes thru to a user-provided filterOptions prop. If no user prop is specified it falls back to a default implementation.
So if you pass AsyncCreatable a filterOptions prop then it will get relayed to Creatable which will then pass its own filterProp pointer along to the child function which should still defer to your filterOptions prop at runtime.
Am I misreading?
@bvaughn Yeah it's relayed to Creatable, but not to the Select since you override everything from Creatable with Async see here: https://github.com/JedWatson/react-select/blob/master/src/AsyncCreatable.js#L23
function reduce(obj, props = {}){
return Object.keys(obj)
.reduce((props, key) => {
const value = obj[key];
if (value !== undefined) props[key] = value;
return props;
}, props);
}
reduce({a: 1}, reduce({a: 2}, {})) // => {a: 1}
Hm. I'm not sure why the order of override properties were reversed since I first created that component.
Anyway, I see. The user's filterOptions function is passed to both the inner Async and the inner Creatable then later reduced so that the Async props override the Creatable props. This means that Creatable can't hook into this process like it needs to.
I think one fix would be similar to what we do with onInputChanged:
<Select
{...asyncProps}
{...creatableProps}
filterOptions={creatableProps.filterOptions}
@bvaughn Oh sorry, I didn't blame the file... Yup, it has been reversed later.
Yeah, I came out with the same fix, unfortunately, it's not enough.
@unity The change was made here https://github.com/JedWatson/react-select/pull/1374 Why did you reverse the props override asyncProps <==> creatableProps? Also, reduce seems to do nothing more.
I updated the demo with the proposed fix, but it's just a first step, look at my GIF.
https://embed.plnkr.co/ik8qaDyOUsa1usQQktaE/
filterOptions={(i) => i}
no filterOptions
Interesting, thanks for looking into this further!
I can't think of any real use case where using an identity function as the filterOptions prop for Creatable would be useful. The buggy result is an interesting observation and fixing it would be great, but the current solution certainly leaves AsyncCreatable in a better state than it was.
After playing with the updated demo I believe this resolves the main issue, and I'd be willing to use it. Cheers! 馃帀
In my case, I need to use AsyncCreatable without any filters.
I wanna have all the options returned by the server + the create option (if not already inside results).
Search: bu
Results: Blue Users, Bus, _Add:_ bu
Search: bus
Results: Blue Users, Bus
Search: 100 _(by ID or whatever)_
Results: Bus, _Add:_ 100
So it's not solved for me 馃槥
I must have taken too quick a look earlier, because you're absolutely right. It's for sure an issue still.
It seems like fixing the bug that is allowing multiple "create option" items would finally resolve this. I would think Creatable should be responsible for cleaning up after itself, not just relying on a filter to hide a growing list of irrelevant options. I'll try to work through some of the code myself to see if I can make sense of the current behavior.
The default filtering on async lookups has always confused me. Seems if you're hitting a remote endpoint for options, it will likely return filtered results. If the endpoint does no filtering, it's probably best to preload the data in memory and just use the standard Select with an options prop. So, given async lookups are pre filtered, why do the Async component variants still perform client-side filtering on user input? In my experience the redundancy has just created extra hassle with no real value, and leads to using an identity filter to just show everything.
Being a relatively new user of this component, I wish I understood the motivation for some of these quirks. Any insights/pointers would be appreciated.
@MilllerTime Regarding the filtering option with Async, there are some cases where it's useful.
Let's say you can select some options but no more than 3 of the same category. You filter server side based on the input, and client side based on the currentValues. Things can be more complex than a simple autocomplete.
I don't have much time to look into Creatable now, maybe next week.
@tcoyze Nothing other than my previous comment. Haven't looked since.
A way to get this working is not to use the AsyncCreatable Component. I face exactly the same problem with @oomathias in which I should use no filter and let the server to do the filter.
What I end up doing is creating my own custom filterOptions
filterOptionsCreatable = (options, filter, excludeOptions, props) => {
var filteredOptions = options.slice()
if (filteredOptions.length === 0) {
filteredOptions.push({[this.props.labelKey]: `Add ${filter}`, [this.props.valueKey]: `Add ${filter}`, create:true});
}
return filteredOptions;
}
Hope this might help
@stephennyu The function doesn't seem to work for me, the creatable option ends up being something like "Add new item";
Were you able to get rid of the Add string in your created options?
I ended up removing the Add ${filter} and just using ${filter} for now.
I know this is old, but I am using Select.AsyncCreatable and have the issue @oomathias showed in the gif where "create new" duplicates over an over. I fixed it by passing the following function to the filterOptions on Select.AsyncCreatable and everything works. I'm sure there are better ways but it works for me.
let filterOptions = options => {
options = options.filter(o => !/Create option/i.test(o.value));
return options;
}
@rowlinsonmike thanks this worked! Mine ended up looking like
filterOptions={options => options.filter(o => !/Create option/i.test(o.label))}
Hello -
In an effort to sustain the react-select project going forward, we're closing old issues / pull requests.
We understand this might be inconvenient but in the best interest of supporting the broader community we have to direct our limited efforts to maintain the latest version.
If you aren't using the latest version of react-select please consider upgrading to see if it resolves any issues you're having.
If you feel this issue / pull request is still relevant and you'd like us to review it, please leave a comment and we'll do our best to get back to you.
Most helpful comment
@bvaughn Oh sorry, I didn't blame the file... Yup, it has been reversed later.
Yeah, I came out with the same fix, unfortunately, it's not enough.
@unity The change was made here https://github.com/JedWatson/react-select/pull/1374 Why did you reverse the props override
asyncProps <==> creatableProps? Also,reduceseems to do nothing more._Fix_
I updated the demo with the proposed fix, but it's just a first step, look at my GIF.
https://embed.plnkr.co/ik8qaDyOUsa1usQQktaE/
Filtered
filterOptions={(i) => i}Not filtered
no filterOptions