React-Select Async loadOptions is not loading options properly
The issue is that Lodash's debounce function is not suitable for this. Lodash specifies that
subsequent calls to the debounced function return the result of the last func invocation
Not that:
subsequent calls return promises which will resolve to the result of the next func invocation
This means each call which is within the wait period to the debounced loadOptions prop function is actually returning the last func invocation, and so the "real" promise we care about is never subscribed to.
Instead use a promise-returning debounce function
For example:
import debounce from "debounce-promise";
//...
this.getOptions = debounce(this.getOptions.bind(this), 500);
See full explanation https://github.com/JedWatson/react-select/issues/3075#issuecomment-450194917
I found out that people intend to look for this problem. So i am posting my update portion of code that fix the issue. Converting from async-await to normal callback function fix my issue. Special thanks to Steve and others.
import React from 'react';
import AsyncSelect from 'react-select/lib/Async';
import { loadingMessage, noOptionsMessage } from './utils';
import _ from 'lodash';
class SearchableSelect extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedOption: this.props.defaultValue
};
this.getOptions = _.debounce(this.getOptions.bind(this), 500);
}
handleChange = selectedOption => {
this.setState({
selectedOption: selectedOption
});
if (this.props.actionOnSelectedOption) {
this.props.actionOnSelectedOption(selectedOption.value);
}
};
mapOptionsToValues = options => {
return options.map(option => ({
value: option.id,
label: option.name
}));
};
getOptions = (inputValue, callback) => {
if (!inputValue) {
return callback([]);
}
const { searchApiUrl } = this.props;
const limit =
this.props.limit || process.env['REACT_APP_DROPDOWN_ITEMS_LIMIT'] || 5;
const queryAdder = searchApiUrl.indexOf('?') === -1 ? '?' : '&';
const fetchURL = `${searchApiUrl}${queryAdder}search=${inputValue}&limit=${limit}`;
fetch(fetchURL).then(response => {
response.json().then(data => {
const results = data.results;
if (this.props.mapOptionsToValues)
callback(this.props.mapOptionsToValues(results));
else callback(this.mapOptionsToValues(results));
});
});
};
render() {
const { defaultOptions, placeholder, inputId } = this.props;
return (
<AsyncSelect
inputId={inputId}
cacheOptions
value={this.state.selectedOption}
defaultOptions={defaultOptions}
loadOptions={this.getOptions}
placeholder={placeholder}
onChange={this.handleChange}
noOptionsMessage={noOptionsMessage}
loadingMessage={loadingMessage}
/>
);
}
}
export default SearchableSelect;