Downshift: The menu should close until results not found

1.9k views Asked by At

I'm facing a problem with Reactjs frontend codebase. We were using a library name react-autoauggest to achieve the autocomplete functionality but lead has decided to shift from react-autoauggest to downshift. I read through the document of this and implemented this using a useCombobox hook but he is presenting some issues and telling me to solve these issues without any guidance. I created a clean version of these issues.

Downshift issues

First I'm resolving issue-4 the clear button should clear the input field and close the menu. But when I click on the clear button, the input field is empty but the menu is still open. Can you please give me some guidance on how to to do these things in the downshift?

Here is my codesandbox link filtering data from the array of objects: View the Code sandbox here

App.js:

const App = () => {

    // Array of objects
    const [inputItems, setInputItems] = useState(data);

    // State for all primitive types
    const [value, setValue] = useState('');


    /**
     * It will returns the new filtered array.
     * @param data Array<Object> - The array to iterate over
     * @param inputValue {string} - Your input value
     * @return Array<Object> - Returns the new filtered array
     */
    const filterByName = (data, inputValue) => {
        return data.filter(item => {
            return item.name.toLowerCase().indexOf(inputValue.toLowerCase()) !== -1;
        });
    };

    // props for the combobox
    const comboboxProps = {
        className: 'search has-icons-left has-buttons-right'
    };

    // props for the input
    const inputProps = {
        type: 'text',
        className: 'form-control',
        placeholder: 'Enter the state'
    };

    // props for the menu
    const menuProps = {
        className: 'menu'
    };


    // useComboBox
    const {
        isOpen,
        getComboboxProps,
        getInputProps,
        getMenuProps,
        getItemProps,
        highlightedIndex,
        selectItem,
    } = useCombobox({
        items: inputItems,
        onInputValueChange: ({inputValue}) => {
            setValue(inputValue);
            setInputItems(filterByName(data, inputValue));
        },
        itemToString: (item) => item ? item.name : '',
    });



    return (
        <div className="app">
            <div {...getComboboxProps(comboboxProps)}>
                <input {...getInputProps(inputProps)} />
                <span className="icon is-left"><MarkerIcon/></span>
                {(typeof value === 'string' && value.length > 0) ?
                    (<span className="button is-right">
                        <button className="btn btn-clear" onClick={() => selectItem(null)}>Clear</button>
                    </span>) : null}
                {/* Suggestions */}
                <ul {...getMenuProps(menuProps)}>
                    {isOpen && inputItems.map((item, index) => (
                        <li key={index} {...getItemProps({item, index})}
                            style={highlightedIndex === index ? {backgroundColor: "#f5f5f5"} : null}>
                            {item.name}
                        </li>
                    ))}
                </ul>
            </div>
        </div>
    );
};

export default App;
1

There are 1 answers

4
Peter On BEST ANSWER

#1, To prevent opening if there is no input value:

<input {
  ...getInputProps({
    ...inputProps,
    onKeyDown: e => {
      if(!value) {
        return e.nativeEvent.preventDownshiftDefault = true 
      }
    }
  })
} />

#2 Can be tricky, because if you want to modify the state, the filter will be activated. I would suggest some layout over their input, what displays the inputItems[highlightedIndex] if the highlightedIndex > -1

  const completeValue =
    highlightedIndex > -1 ? inputItems[highlightedIndex].name : null;

 return (
   <div className="app">
     ...
{
  completeValue
  ? <input {...getInputProps(inputProps)} value={completeValue} />
    : (
      <input
        {...getInputProps({
          ...inputProps,
          onKeyDown: e => {
            if (!value) {
              return (e.nativeEvent.preventDownshiftDefault = true);
            }
          }
        })}
      />
    )
}
     ...
   </div>
 )

#3, To close the recommendations box:

  const {
    isOpen,
    getComboboxProps,
    getInputProps,
    getMenuProps,
    getItemProps,
    highlightedIndex,
    closeMenu, // <= use this inbuilt functionality
    selectItem
  } = useCombobox({

And at the button click just call it by manually:

    <button
      className="btn btn-clear"
      onClick={() => {
        selectItem(null);
        setValue("");
        closeMenu();
      }}
    >
      Clear
    </button>