'Options not showing when using custom input in React-Bootstrap-TypeAhead

I am using React-Bootstrap-TypeAhead's latest version in my React project. The main goal is to display the options menu when the user types. The menu is displayed when using the default input component but once I use the render input method for customization the options menu stops showing:

working example

import React, { useState } from 'react';
import { AsyncTypeahead } from 'react-bootstrap-typeahead';

/* example-start */
const BasicExample = ({ key, label }) => {
  const [singleSelections, setSingleSelections] = useState([]);
  const [multiSelections, setMultiSelections] = useState([]);
  const [query, setQuery] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [options, setOptions] = useState([]);

  const PER_PAGE = 50;
  const SEARCH_URI = 'https://api.github.com/search/users';

  function makeAndHandleRequest(query, page = 1) {
    return fetch(`${SEARCH_URI}?q=${query}+in:login&page=${page}&per_page=50`)
      .then((resp) => resp.json())
      .then(({ items, total_count }) => {
        /* eslint-disable-line camelcase */
        const options = items.map((i) => ({
          avatar_url: i.avatar_url,
          id: i.id,
          login: i.login,
        }));
        return { options, total_count };
      })
      .catch((err) => console.log(err));
  }

  const _handleInputChange = (query) => {
    setQuery(query);
  };

  const _handlePagination = (e, shownResults) => {
    const { query } = this.state;
    const cachedQuery = this._cache[query];

    // Don't make another request if:
    // - the cached results exceed the shown results
    // - we've already fetched all possible results
    if (cachedQuery.options.length > shownResults || cachedQuery.options.length === cachedQuery.total_count) {
      return;
    }

    setIsLoading(true);

    const page = cachedQuery.page + 1;

    makeAndHandleRequest(query, page).then((resp) => {
      const options = cachedQuery.options.concat(resp.options);
      // this._cache[query] = { ...cachedQuery, options, page };
      setIsLoading(false);
      setOptions(options);
    });
  };

  const _handleSearch = (query) => {
    setIsLoading(true);
    makeAndHandleRequest(query).then((resp) => {
      setIsLoading(true);
      setOptions(resp?.options || []);
    });
  };

  return (
    <>
      <AsyncTypeahead
        {...{ query, isLoading, options }}
        id="async-pagination-example"
        labelKey="login"
        maxResults={PER_PAGE - 1}
        minLength={2}
        onInputChange={_handleInputChange}
        onPaginate={_handlePagination}
        onSearch={_handleSearch}
        renderInput={({ inputRef, referenceElementRef, ...inputProps }) => (
          <div className="form-group h-64">
            <label>Job Category</label>
            <div className="input-group">
              <input
                type="text"
                {...inputProps}
                ref={(input) => {
                  inputRef(input);
                  // referenceElementRef(input);
                }}
                className="form-control"
                placeholder=""
              />
            </div>
          </div>
        )}
        paginate
        placeholder="Search for a Github user..."
        renderMenuItemChildren={(option) => (
          <div key={option.id}>
            <img
              alt={option.login}
              src={option.avatar_url}
              style={{
                height: '24px',
                marginRight: '10px',
                width: '24px',
              }}
            />
            <span>{option.login}</span>
          </div>
        )}
        useCache={false}
      />
    </>
  );
};
/* example-end */

export default BasicExample;


Solution 1:[1]

The reason you're not seeing any results rendered is that _handleInputChange is triggering a re-render and resetting the debounced onSearch handler before it can fire.

You can wrap _handleSearch with useCallback to fix that:

const _handleSearch = useCallback((query) => {
  setIsLoading(true);
  makeAndHandleRequest(query).then((resp) => {
    setIsLoading(false);
    setOptions(resp?.options || []);
  });
}, []);

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 ericgio