mrkent
mrkent

Reputation: 353

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([]);
    }
  }}
/>

Upvotes: 0

Views: 1337

Answers (2)

vczm
vczm

Reputation: 594

An easier way is to do it by ref.

const ref = createRef<TypeaheadRef>()

const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>): void => {
  if (e.code === 'Enter' && ref.current?.state.activeIndex === -1) {
    // ... 
  }
}

return (
  <Typeahead
    ref={ref}
    id="basic-typeahead-multiple"
    labelKey="name"
    multiple
    onChange={setMultiSelections}
    options={options}
    selected={multiSelections}
    onKeyDown={handleKeyDown}
  />
)

Upvotes: 0

ericgio
ericgio

Reputation: 3509

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>
);

Upvotes: 1

Related Questions