1
votes

I have a material UI autocomplete component in a ReactJS application which I desire to have the following behavior:

  1. The user can type input into the autocomplete, and when the user presses enter without scrolling through the suggested choices, event A is fired with the input typed in the autocomplete as a parameter.
  2. The user can scroll through the suggested choices (with arrow keys), and when the user presses enter, event B is fired with the currently selected autocomplete choice in the parameter
  3. The user can scroll through the suggested choices (with arrow keys/mouse scroller), and when the user clicks one of the suggestions, event B is fired with the clicked autocomplete choice in the parameter

I was able to make these behaviors individually. I handled the first behavior with an onKeyDown waiting for an enter key, and the second + third behavior with an onChange as suggested here: stackoverflow.com/questions/58666189/getting-the-value-in-the-react-material-ui-autocomplete.

The problem appears when I put both events together. When I attempt to scroll through the choices with arrow keys and press enter (behavior 2), both the onKeyDown and onChange events get fired. This fires BOTH event A with what the user typed and event B with what the user selected, when I only want to fire event B with what the user selected. Is there a way to detect if the user has begun scrolling through the autocomplete suggestions with arrow keys (so that I can wrap event A in an if condition), or an alternative way to look at this problem to resolve it?

The Autocomplete component:

<Autocomplete options={someArrayOfStrings}
                            onChange={this.submitComment} // event B
                            onKeyDown={this.submitCommentEnter} // event A
                            value={this.state.userInput} // what the user has typed
                            autoComplete
                            freeSolo
                            renderInput={(params) => <TextField {...params}
                                                                fullWidth
                                                                label="Search"                                                           
                                                                variant="outlined"/>}/>

Event A (submitCommentEnter):

submitCommentEnter(e) {
        if (e.key == 'Enter') { // possibly add check if user is scrolling through autocomplete with arrow keys here?
            /* content of event A here; uses this.state.userInput */
        }
    }

Event B (submitComment):

submitComment(event, value) {
        if (value !== null) {
            /* content of event B here; uses value */
        }
    }
1

1 Answers

2
votes

You don't need to handle the keyDown event, You just need to use the reason of on-change event as per document onChange provide three params

 onChange={(event, value, reason) => {
          console.log("onChange", reason, value, event.currentTarget);
 }}

The reason can be of 5 types "create-option", "select-option", "remove-option", "blur" or "clear". when you press enter in text field reason will be create-option and when you select an option from the drop-down the reason will be select-options, below is the complete code to verify the fact.

import React from "react";
import TextField from "@material-ui/core/TextField";
import Autocomplete from "@material-ui/lab/Autocomplete";

const someArrayOfStrings = ["test", "test2", "test3"];

export default function CheckboxesTags() {
  const [selectedValues, setSelectedValues] = React.useState();
  return (
    <React.Fragment>
      <Autocomplete
        options={someArrayOfStrings}
        onChange={(event, value, reason) => {
          console.log("onChange", reason, value, event.currentTarget);
          // setSelectedValues(value);
        }}
        value={selectedValues} // what the user has typed
        autoComplete
        freeSolo
        renderInput={params => (
          <TextField {...params} fullWidth label="Search" variant="outlined" />
        )}
      />
    </React.Fragment>
  );
}

I have also created this code-sandbox project, where you can check the console for