react-bootstrap-typeahead

react-bootstrap-typeahead How do I trigger a callback (update state) on `Enter` but not when the user is selecting hint?


I'm trying to make an input that will allow user to select multiple items. When finished selecting, he can press Enter to push his selections into an array in the state. The input will then clear and be ready for his next set of selections. (Imagine populating a 2-d array quickly). However, with the code below, if the user presses Enter to select the hint, my callback, pushToState, will trigger as it's in onKeyDown. What's the correct way to implement this behavior?

<Typeahead
  id="basic-typeahead-multiple"
  labelKey="name"
  multiple
  onChange={setMultiSelections}
  options={options}
  selected={multiSelections}
  onKeyDown={(event) => {
    if (event.key === 'Enter') {
      pushToState(multiSelections);
      setMultiSelections([]);
    }
  }}
/>

Solution

  • Your use case is similar to the one in this question. You basically need to determine whether the user is selecting a menu item or not. You can do that by checking the activeIndex of the typeahead:

    // Track the index of the highlighted menu item.
    const [activeIndex, setActiveIndex] = useState(-1);
    
    const onKeyDown = useCallback(
      (e) => {
        // Check whether the 'enter' key was pressed, and also make sure that
        // no menu items are highlighted.
        if (event.key === 'Enter' && activeIndex === -1) {
          pushToState(multiSelections);
          setMultiSelections([]);
        }
      },
      [activeIndex]
    );
    
    return (
      <Typeahead
        id="basic-typeahead-multiple"
        labelKey="name"
        multiple
        onChange={setMultiSelections}
        options={options}
        onChange={setMultiSelections}
        onKeyDown={onKeyDown}
        selected={multiSelections} >
        {(state) => {
          // Passing a child render function to the component exposes partial
          // internal state, including the index of the highlighted menu item.
          setActiveIndex(state.activeIndex);
        }}
      </Typeahead>
    );