React-autosuggest: When hitting enter to select a suggestion, the input does not clear.

Created on 15 Dec 2016  路  4Comments  路  Source: moroshko/react-autosuggest

This is a very specific bug. It occurs when:

  1. focusFirstSuggestion is set to true.
  2. The input is to be cleared when a suggestion is selected.

    • i.e. onSuggestionSelected() contains this.setState({ value: '' });.

  3. You hit Enter to select the first suggestion that was automatically focused onto.
  4. The first suggestion must not match exactly the thing that was typed in.

Demo

This pen has two major differences with the basic one:

  1. focusFirstSuggestion is set to true.
  2. A function is passed in into the onSuggestionSelected prop:

    onSuggestionSelected = () => {
      this.setState({ value: '' })
    };
    

Try it here.

  1. Focus on the input field.
  2. Type c (lowercase), and wait for suggestions to appear.
  3. Press Enter to select the first suggestion.

    Observed behaviour:

Note that the c remains, and the input is not cleared.

Expected behaviour:

The input should be cleared, since there is already a focus on the first item of the suggestions. And we have onSuggestionSelected set to clear the input for us. Unfortunately, onChange is being called after onSuggestionSelected when it should not be.

The Problem

When the suggestion is selected by hitting Enter, the onSuggestionSelected function is called, and the input is properly cleared. However, immediately after that, maybeCallOnChange is called.

Line 369 to 376:

this.onSuggestionSelected(event, {
  suggestion: focusedSuggestion,
  suggestionValue: newValue,
  sectionIndex: focusedSectionIndex,
  method: 'enter'
});

this.maybeCallOnChange(event, newValue, 'enter');

This takes us to the definition on line 160 for maybeCallOnChange():

maybeCallOnChange(event, newValue, method) {
  const { value, onChange } = this.props.inputProps;

  if (newValue !== value) {
    onChange(event, { newValue, method });
  }
}

Since the current value was typed in by the user, and the newValue came from the selected suggestion, onChange is called with the newValue. This leads us to our simple onChange function:

onChange = (event, { newValue, method }) => {
  this.setState({ value: newValue });
}

As you can see, it just sets the value to the state and you end up with the new suggestion in the input rather than a cleared box.

The (temporary) Solution

In the onChange handler function that we pass down to the Autosuggest component, we must first check if the method is enter. If it is, then prevent the function from continuing:

onChange = (event, { newValue, method }) => {
  // prevent change if artist was just selected
  if (method === 'enter') return;
  // otherwise, let the change go through
  this.setState({ value: newValue });
}

A Permanent Solution

We need to prevent onChange from being called after onSuggestionSelected when:

  1. focusFirstSuggestion is set to true, and;
  2. The user pressed Enter to trigger onSuggestionSelected.

Most helpful comment

@adrianmcli What a great example of a properly submitted PR!

Thanks a lot for taking the time to explain the problem and fixing it 馃憤

All 4 comments

@adrianmcli What a great example of a properly submitted PR!

Thanks a lot for taking the time to explain the problem and fixing it 馃憤

v7.0.2 includes this fix.

@adrianmcli, thank you very much, you saved my day.

I was performing a API Call, inside the onChange event, to bring all the possible "suggestions".
But, because of that, every time I clicked one of the suggestions, it was triggering the onChange again, and creating another API Call.
After reading your explanation, about your scenario, I was able to solve my problem this way:

`handleChange = (event, { newValue, method }) => {
if (method === 'click') return;
this.setState({
value: newValue
});

if(this.timeout) { clearTimeout(this.timeout); }
this.timeout = setTimeout(() => {
  this.props.getSearchForItems(newValue);
}, 300);

}`

@marcos-ld thanks for the kind comments! It's been 1.5 years since the PR was merged, and since then I've actually met @moroshko in person at ReactConf too haha. Always nice to receive these kinds of comments 馃憤

It does kind of make me sad that you had to do this workaround though, I wonder if there's a way to make it less implicit. At the end of the day, it seems that onChange is sometimes getting called when the user doesn't really want to call it. Maybe there should be a separate onSelectionChanged that is called? Hmmm...

Was this page helpful?
0 / 5 - 0 ratings