Combo box with async search
This demonstrates a combo box with an async search. The search will return the results of the token search randomly between 0 and 1 seconds after typing.
You can adjust the debounce options. Some screen-readers become overlay chatty if aria-busy
is toggled too often.
import { useMemo, useState } from 'react';
import { ComboBox, useAsyncSearch, tokenSearcher } from '@citizensadvice/react-combo-boxes';
import countries from '../../data/countries.json';
function mapOption({ name }) {
return name;
}
function useFindOptions(query, { debounce }) {
const searcher = useMemo(() => {
const search = tokenSearcher(countries, { index: mapOption });
return async (term) => {
await new Promise((resolve) => {
setTimeout(resolve, Math.random() * 1000);
});
return search(term);
};
}, []);
return useAsyncSearch(query, { searcher, debounce });
}
export function Example() {
const [autoselect, setAutoselect] = useState('inline');
const [value, setValue] = useState(null);
const [search, setSearch] = useState(null);
const [searchDebounce, setSearchDebounce] = useState(200);
const [busyDebounce, setBusyDebounce] = useState(400);
const [filteredOptions, busy] = useFindOptions(search, {
debounce: searchDebounce,
});
return (
<>
<label
id="select-label"
htmlFor="select"
>
Select
</label>
<ComboBox
id="select"
aria-labelledby="select-label"
value={value}
onValue={setValue}
onSearch={setSearch}
options={filteredOptions}
mapOption={mapOption}
busy={busy}
busyDebounce={busyDebounce}
autoselect={autoselect}
/>
<label htmlFor="busy-debounce">
Busy debounce
{`(${busyDebounce})`}
</label>
<input
id="busy-debounce"
onChange={({ target: { value: v } }) => setBusyDebounce(+v)}
value={busyDebounce}
type="range"
min={0}
max={1000}
step={100}
/>
<label htmlFor="search-debounce">
Search debounce
{`(${searchDebounce})`}
</label>
<input
id="busy-debounce"
onChange={({ target: { value: v } }) => setSearchDebounce(+v)}
value={searchDebounce}
type="range"
min={0}
max={1000}
step={100}
/>
<fieldset>
<legend>Autoselect</legend>
<label>
<input
type="radio"
name="autoselect"
checked={autoselect === false}
onChange={({ target: { checked } }) => {
if (checked) {
setAutoselect(false);
}
}}
/>{' '}
<code>false</code>
</label>
<label>
<input
type="radio"
name="autoselect"
checked={autoselect === true}
onChange={({ target: { checked } }) => {
if (checked) {
setAutoselect(true);
}
}}
/>{' '}
<code>true</code>
</label>
<label>
<input
type="radio"
name="autoselect"
checked={autoselect === 'inline'}
onChange={({ target: { checked } }) => {
if (checked) {
setAutoselect('inline');
}
}}
/>{' '}
<code>"inline"</code>
</label>
</fieldset>
<label htmlFor="output">Current value</label>
<output
htmlFor="select"
id="output"
>
{JSON.stringify(value, undefined, ' ')}
</output>
</>
);
}