This is a very specific bug. It occurs when:
focusFirstSuggestion is set to true.onSuggestionSelected() contains this.setState({ value: '' });.This pen has two major differences with the basic one:
focusFirstSuggestion is set to true.A function is passed in into the onSuggestionSelected prop:
onSuggestionSelected = () => {
this.setState({ value: '' })
};
Try it here.
c (lowercase), and wait for suggestions to appear.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.
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.
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.
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 });
}
We need to prevent onChange from being called after onSuggestionSelected when:
focusFirstSuggestion is set to true, and;onSuggestionSelected.@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...
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 馃憤