I have an input element where client starts typing an item name and it performs a search on backend over API. I get a list of results and client choses the item he wants.
In desktop browser my code works perfectly and I get my dropdown list of items, but when I tested it on iPhone I see no results, only the loader.
I prefer defining states and values in objects, like so:
interface IItem { ... }
type List = IItem[]
interface IState {
typing: boolean;
loading: boolean;
open: boolean;
timeout: ReturnType<typeof setTimeout>;
// ...
}
interface IValue {
item: IItem;
list: List;
// ...
}
const [state, setState] = React.useState<IState>({
typing: false,
loading: false,
open: false,
timeout: null,
// ...
});
const [value, setValue] = React.useState<IValue>({
item: { ... },
list: [],
// ...
});
This is my handler. Basically what it does it sets a timeout for 500ms to prevent calling API immediately user types a character and let him finish typing his query.
const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
if (state.timeout) {
clearTimeout(state.timeout);
}
setState({
...state,
typing: true,
timeout: setTimeout(async () => {
setValue({
...value,
list: await searchItem(event.target.value),
});
setState({ ...state, typing: false });
}, 500),
});
};
const searchItem = async (query: string) => {
setState({ ...state, loading: true });
const result = await fetch("my-api-endpoint");
const list: List = await result.json();
setState({ ...state, loading: false });
return list;
};
For styling I use Material-UI, this is another handler for Autocomplete component:
const handleSelect = (
_event: React.MouseEvent<HTMLElement> | React.TouchEvent<HTMLElement>,
selected: IItem
) => {
setValue({
...value,
item: selected ?? { /* default values */ },
});
};
And finally this how my MUI component looks like:
<Autocomplete
value={value.item}
onChange={handleSelect}
open={state.open}
onOpen={() => setState({ ...state, open: true })}
onClose={() => setState({ ...state, open: false })}
getOptionSelected={(option, value) => option?.symbol === value?.symbol }
options={value.list}
loading={state.loading}
renderInput={(params) => (
<TextField
{...params}
onChange={handleChange}
/>
)}
/>
As I mentioned at the beginning, my code works as expected on desktop, the problem is with iOS (didn't test Android). What am I missing?