'react-select debounced async call not displaying suggestions
I'm using react-select loading the results from an api and debouncing the queries with lodash.debounce:
import React, {useState} from 'react';
import AsyncSelect from 'react-select/lib/Async';
import debounce from 'lodash.debounce';
import {search} from './api';
const _loadSuggestions = (query, callback) => {
return search(query)
.then(resp => callback(resp));
};
const loadSuggestions = debounce(_loadSuggestions, 300);
function SearchboxTest() {
const [inputValue, setInputValue] = useState("");
const onChange = value => {
setInputValue(value);
};
return (
<AsyncSelect
value={inputValue}
loadOptions={loadSuggestions}
placeholder="text"
onChange={onChange}
/>
)
}
It seems to work fine when I enter something in the searchbox for the first time (query is debounced and suggestions populated correctly), but if I try to enter a new value, a second fetch query is fired as expected, but the suggestions coming from that second call are not displayed (and I don't get the "Loading..." message that react-select displays).
When I don't debounce the call the problem seems to go away (for the first and any subsequent calls):
import React, {useState} from 'react';
import AsyncSelect from 'react-select/lib/Async';
import {search} from '../../api';
const loadSuggestions = (query, callback) => {
return search(query)
.then(resp => callback(resp));
};
function SearchboxTest() {
const [inputValue, setInputValue] = useState("");
const onChange = value => {
setInputValue(value);
};
return (
<AsyncSelect
value={inputValue}
loadOptions={loadSuggestions}
placeholder="text"
onChange={onChange}
/>
)
}
Any idea what is going on? Any help to understand this issue would be much appreciated.
M;
Solution 1:[1]
I was aslo facing the same issue and got this solution.
If you are using callback function, then You DON'T need to return the result from API.
Try removing return keyword from _loadSuggestions function. as shown below
import React, {useState} from 'react';
import AsyncSelect from 'react-select/lib/Async';
import debounce from 'lodash.debounce';
import {search} from './api';
const _loadSuggestions = (query, callback) => {
search(query)
.then(resp => callback(resp));
};
const loadSuggestions = debounce(_loadSuggestions, 300);
function SearchboxTest() {
const [inputValue, setInputValue] = useState("");
const onChange = value => {
setInputValue(value);
};
return (
<AsyncSelect
value={inputValue}
loadOptions={loadSuggestions}
placeholder="text"
onChange={onChange}
/>
)
}
Solution 2:[2]
Use react-select-async-paginate package - This is a wrapper on top of react-select
that supports pagination. Check it's NPM page
React-select-async-paginate works effectively with internal denounce. You can pass debounce interval in the props.
<AsyncPaginate
value={value}
loadOptions={loadOptions}
debounceTimeout={300}
onChange={setValue}
/>
Here is codesandbox example
Solution 3:[3]
Maybe this will help someone.
freeze = false //mark delay
timer //saved timer
loadAddress = async (strSearch: string) => {
this.freeze = true //set mark for stop calls
return new Promise(async (res, err) => { //return promise
let p = new Promise((res, err) => {
if(this.freeze) clearTimeout(this.timer) //remove prev timer
this.timer = setTimeout(async () => {
this.freeze = false
const r = await this.load(strSearch)//request
res(r);
}, 2000)
})
p.then(function (x) {
console.log('log-- ', x);
res(x);
})
});
};
Solution 4:[4]
What worked for me was, instead of making use of the default debounce from lodash, making use of the debounce-promise package:
import React from 'react';
import debounce from 'debounce-promise';
import AsyncSelect from 'react-select/async';
const debounceFunc = debounce(async () => {
return [] // SelectOptions
}, 200);
export function MyComponent() {
return <AsyncSelect loadOptions={debounceFunc} defaultOptions />
}
Solution 5:[5]
In my solution help me following. I have AsyncSelect where i want data from https://github.com/smeijer/leaflet-geosearch. My provider is:
const provider = new OpenStreetMapProvider({
params: {
countrycodes: "cz",
limit: 5
}
});
const provider
you will see below one more time.
<AsyncSelect className="map-search__container"
classNamePrefix="map-search"
name="search"
cacheOptions
components={{DropdownIndicator, IndicatorSeparator, Control, NoOptionsMessage, LoadingMessage}}
getOptionLabel={getOptionLabel}
getOptionValue={getOptionValue}
loadOptions={getData}
onChange={handleChange}
isClearable
placeholder={inputSave || ""}
value=""
inputValue={input}
onMenuClose={handleMenuClose}
onInputChange={handleInputChange}
onFocus={handleFocus}
blurInputOnSelect
defaultOptions={true}
key={JSON.stringify(prevInputValue)}
/>
cachceOptions
- is basiccomponents
- my componentsloadOptions
- is the most important!
anything else is not important
const getData = (inputValue: string, callback: any) => {
debouncedLoadOptions(inputValue, callback);
}
const debouncedLoadOptions = useDebouncedCallback(fetchData, 750);
import {useDebouncedCallback} from 'use-debounce';
This use was my key for solution. I tried debounce from lodash, but i had still problem with not working debouncing. This use help me and did my problem solve.
const fetchData = (inputValue: string, callback: any) => {
return new Promise((resolve: any) => {
resolve(provider.search({query: inputValue || prevInputValue}));
}).then((json) => {
callback(json);
});
};
prevInputValue
is props from parent... (you don't need it)
Callbacks was second key. First was useDebouncedCallback.
#sorryForMyEnglish
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Avkash |
Solution 2 | |
Solution 3 | |
Solution 4 | wilbo |
Solution 5 | Ji?í Zeman |