dsudo
dsudo

Reputation: 193

Material-ui Autocomplete - onChange not triggered when updating value in onHighlightChange

I've been working on an extended version of Material UI's Autocomplete where I am implementing a feature that allows the user to move the option to the input via keyboard events (Arrow key up + down). The user should then be allowed to select one of the options via the ENTER key.

For some reason, the onChange event is not triggered and I am kind of puzzled to understand why this happens.

export default function Autocompleter() {
  const [input, setInput] = React.useState(null);

  const handleInputChange = (event, option, reason) => {
    console.log('On input change triggered');
  };

  const handleOnChange = (event, value, reason) => {
    console.log('On change triggered! ');
  };

  const handleHighlightChange = (event, option, reason) => {
    if (option && reason === 'keyboard') {
      setInput(option);
    }
  };

  const handleFilterOptions = (currentOptions) => currentOptions;

  const handleGetOptionsLabel = (option) => {
    return option.label;
  };

  return (
    <Autocomplete
      id="combo-box-demo"
      freeSolo={true}
      value={input}
      onChange={handleOnChange}
      onInputChange={handleInputChange}
      options={top100Films}
      isOptionEqualToValue={(option, value) => option.label === value.label}
      includeInputInList={true}
      onHighlightChange={handleHighlightChange}
      getOptionLabel={handleGetOptionsLabel}
      filterOptions={handleFilterOptions}
      style={{ width: 300 }}
      renderInput={(params) => (
        <TextField {...params} label="Combo box" variant="outlined" />
      )}
    />
  );
}

Here is also a working example:

https://stackblitz.com/edit/react-ts-rsodyc?file=index.tsx,App.tsx,Autocompleter.tsx

NOTE: This is a light example of my original code, but it should be enough to address the issue.

There are a few things I tried such as using inputValue in combination with the onHighlightChange but this does not seem to work either.

includeInputInList seemed to be the solution according to the doc, but it does nothing? Does anyone understand what it is supposed to do and is it helpful in my case?

UPDATE:

Updating the input state in onHighlightChange breaks the onChange. Unfortunately, I do want to update the input every time the user highlights an option via keyboard events.

Thank you for any kind of help and idea

Upvotes: 3

Views: 8682

Answers (2)

RubenSmn
RubenSmn

Reputation: 4642

Since I found that to my knowledge its not possible to check for a "Enter" key on the handleHighlightChange function I've come up with this.

highlightedInput is a seperate state for the highlighted value, this way you can keep track of the currently highlighted input. We set this in the handleHighlightChange after our checks.

We want to change our input state when we click Enter, normally when clicking the Enter key the dropdown closes. To handle this we can create a state for the open state of the dropdown. For this we need a handleOpen and a custom close handler handleOnclose here we can set the currently highlighted value (highlightedInput) to the actual input state.

const [input, setInput] = React.useState(null);
const [isOpen, setIsOpen] = React.useState(false);
const [highlightedInput, setHighlightedInput] = React.useState(null);

const handleOpen = () => {
  setIsOpen(true);
};

const handleOnClose = (event, option, reason, details) => {
  if (option && event.key === "Enter") {
    setInput(highlightedInput);
  }
  setIsOpen(false);
};

const handleInputChange = (event, option, reason) => {
  console.log("On input change triggered");
};

const handleOnChange = (event, value, reason) => {
  console.log("On change triggered!");
};

const handleHighlightChange = (event, option, reason) => {
  if (option && reason === "keyboard") {
    setHighlightedInput(option);
  }
};

const handleFilterOptions = (currentOptions) => currentOptions;

const handleGetOptionsLabel = (option) => {
  return option.label;
};

Note that we changed the value from the AutoComplete to the highlightedInput instead of input.

return (
  <React.Fragment>
    <Autocomplete
      id="combo-box-demo"
      freeSolo={true}
      open={isOpen}
      onOpen={handleOpen}
      onClose={handleOnClose}
      value={highlightedInput}
      onChange={handleOnChange}
      onInputChange={handleInputChange}
      options={top100Films}
      isOptionEqualToValue={(option, value) => option.label === value.label}
      includeInputInList={true}
      onHighlightChange={handleHighlightChange}
      getOptionLabel={handleGetOptionsLabel}
      filterOptions={handleFilterOptions}
      style={{ width: 300 }}
      renderInput={(params) => (
        <TextField {...params} label="Combo box" variant="outlined" />
      )}
    />
    <div style={{ height: "200px" }}></div>
    {input?.label}
  </React.Fragment>
);

Live version

Upvotes: 3

Bojan Tomić
Bojan Tomić

Reputation: 1085

The onChange handler runs when the user selects an option from the drop down. Seams that you want the onInputChange event. That one fires when you type in the input field.

Upvotes: 1

Related Questions