Autosuggest with Rest API issue on React
Published: 2023-07-22 09:18:45

I use `Autosuggest` component (https://github.com/moroshko/react-autosuggest) in my `React` website. So, on the `onChange` event, I call `serviceOsago` to get the city/region/country data. Full data are too heavy to load at once, it takes about 10 seconds, so I get it partially by using `onChange` event. But the problem is when I type a city name for example: "Стрий", it does not add it to the `Autosuggest` and does not display it.

      const onChange = (event, {newValue, method}) => {
        setValue(newValue);
        serviceOsago.getCityCode(newValue).then((value:any) => {
          setCities(value);
        });
      };

When debugging it in the browser (Network -> Fetch/XHR), I found out it fails to add "Стрий" to Autosuggest due to request delay. For example, when I typed "Стрий", it still loads the "Стри" old call.

**Code:**

    import React, { useState } from 'react';
    import Autosuggest from 'react-autosuggest';
    import {useDispatch, useSelector} from 'react-redux';
    import {
      getErrorCity,
      getLoadingCity,
      getRegisterCity,
      setData,
      setError
    } from '../../../redux-data/city/cityReducer';
    import {getInstanceError} from '../../../utils/getInstanceError';
    import theme from './AutoComplete.module.css';
    import SquareLoader from 'react-spinners/SquareLoader';
    import {config} from '../../../assets/config';
    import serviceOsago from '../../../service/serviceOsago';
    
    export const AutoComplete = (props) => {
      const [touch, setTouch] = useState(false);
      const dispatch = useDispatch();
      const regCity = useSelector(getRegisterCity);
      const loading = useSelector(getLoadingCity);
      const [suggestions, setSuggestions] = useState([] as any[]);
      const [value, setValue] = useState(regCity);
      const [cities, setCities] = useState([]);
    
      const getSuggestions = (value, cities) => {
        let res = []; 
        const inputValue = value.trim().toLowerCase();
        const inputLength = inputValue.length;
    
        if (inputValue && inputLength > 0) {
            res = cities.filter(city => {
              return city.name.trim().toLowerCase().startsWith(inputValue);
            });
        }
    
        return res;
      };
    
      const renderSuggestion = suggestion => (
        <div>
          {suggestion.nameFull}
        </div>
      );
    
      const getSuggestionValue = suggestion => suggestion.nameFull;
      const error = useSelector(getErrorCity);
      const errors = {
        regCity: error
      };
      const {
        getClassError,
        getMessageError
      } = getInstanceError(errors);
      const mes = getMessageError('regCity');
    
      const onSuggestionsFetchRequested = ({ value }) => {
        setSuggestions(getSuggestions(value, cities));
      };
    
      const onChange = (event, {newValue, method}) => {
        setValue(newValue);
        console.log(newValue);
        serviceOsago.getCityCode(newValue).then((value:any) => {
          setCities(value);
        });
      };
    
      const onBlur = () => {
        setTouch(true);
        if (value === '') {
            dispatch(setError({
              message: 'Це поле обов\'язкове'
            }));
        }
      };
    
      const inputProps = {
        placeholder: 'Місто реєстрації автомобіля',
        value,
        onChange,
        onBlur,
        disabled: loading,
        id: props.id
      };
    
      const renderSuggestionInput = (inputProps) => (
        props.isAutoFocus ? <input {...inputProps} autoFocus /> : <input {...inputProps} />
      );
    
      const onSuggestionHighlighted = ({ suggestion }) => {
        //console.log(suggestion);
      };
    
      return (
        <div className={getClassError('regCity', touch)}>
          <Autosuggest 
            suggestions={suggestions}
            onSuggestionsFetchRequested={onSuggestionsFetchRequested}
            getSuggestionValue={getSuggestionValue}
            renderSuggestion={renderSuggestion}
            inputProps={inputProps}
            onSuggestionSelected={(event, data) => {
              dispatch(setData(data.suggestion));
            }}
            theme={theme}
            renderInputComponent={renderSuggestionInput}
            highlightFirstSuggestion={true}
            alwaysRenderSuggestions={true}
            onSuggestionHighlighted={onSuggestionHighlighted}
          />
          {mes && <p>{mes}</p>}
          <SquareLoader loading={loading} size={20} color={config.color} css={config.css}/>
        </div>
      );
    };

I think, there are two options how to fix this issue.
1. Load full data when website is loaded, save somewhere this data (for example: `localStorage`) and add it to `Autosuggest`, so it will be available when user is typing. But I think `localStorage` will fail to save 12 - 15 MB of data. There must be another way to store huge data in `React/TypeScript`?

2. Change/improve the `onChange` event, so it will only trigger `API` request when a user finished typing and get the full city word instead of calling request on every character. Is it possible to override the `onChange` event?

What is the proper way to add data from `REST API` to `Autosuggest` when a user typing data in `React/TypeScript`? Thank you.