Andrew Ooi
Andrew Ooi

Reputation: 349

MUI Autocomplete (multiple) controlled values - mysterious input behavior

I am trying to write code to asynchronously search a multiple-select combo upon keyboard entry.

However I found in latest version (5.2.2) a strange behaviour where I cannot explain. I distill the issue below (based on example from MUI's autocomplete page):

import * as React from "react";
import TextField from "@mui/material/TextField";
import Autocomplete from "@mui/material/Autocomplete";

const options = [
  { label: "Option 1", value: 1 },
  { label: "Option 2", value: 2 }
];

export default function ControllableStates() {
  // const [value, setValue] = React.useState<any | null>([]);
  const value = [];
  const [inputValue, setInputValue] = React.useState("");

  console.log("Current Value:", value);

  return (
    <div>
      <div>{`value: ${value !== null ? `'${value}'` : "null"}`}</div>
      <div>{`inputValue: '${inputValue}'`}</div>
      <br />
      <Autocomplete
        multiple={true}
        value={value}
        onChange={(event: any, newValue: any | null) => {
          //setValue(newValue);
        }}
        inputValue={inputValue}
        onInputChange={(event, newInputValue) => {
          setInputValue(newInputValue);
        }}
        id="controllable-states-demo"
        options={options}
        sx={{ width: 300 }}
        renderInput={(params) => <TextField {...params} label="Controllable" />}
      />
    </div>
  );
}


The codeSandbox is as follows: https://codesandbox.io/s/controllablestates-material-demo-forked-ygqp2?file=/demo.tsx

If you try in the codeSandbox, you will be unable to type anything in the TextField field.

However, if you switch the commenting:

  const [value, setValue] = React.useState<any | null>([]);
  // const value = [];

You will be able to type in the TextField field. What is actually happening here? The value did not change at all.

Can anyone figure out why my first code (where the value is a const empty array) didn't work?

The reason I am asking is that I need to pass in the (controlled) value as props, and then set it to default to [] if it is null. I find that I am unable to type in the TextField due to this defaulting.

Upvotes: 3

Views: 19770

Answers (2)

DINA TAKLIT
DINA TAKLIT

Reputation: 8428

If you are using react-hook-form you can set up the autocomplete by using

  • multiple to add multiple values,
  • options: you add the options to be selected
  • getOptionLabel: to show up the label of the options
  • onChange: use onChange function of react-hook-form to set the selected values
  • renderInput: to render the input
import { useForm, Controller } from 'react-hook-form'
import {
  Box,
  TextField,
  Autocomplete,
} from '@mui/material'

const {
  ...
  control,
  formState: { errors },
} = useForm()

<Box mt={2}>
  <Controller
    control={control}
    name="industries"
    rules={{
      required: 'Veuillez choisir une réponse',
    }}
    render={({ field: { onChange } }) => (
      <Autocomplete
        defaultValue={
          useCasesData?.industries
            ? JSON.parse(useCasesData?.industries)
            : []
        }
        multiple
        disableCloseOnSelect
        options={companyIndustryTypes}
        getOptionLabel={(option) => option.name}
        onChange={(event, values) => {
          onChange(values)
        }}
        renderInput={(params) => (
          <TextField
            {...params}
            label="Type d'industries"
            placeholder="Type d'industries"
            helperText={errors.industries?.message}
            error={!!errors.industries}
          />
        )}
      />
    )}
  />
</Box>

Note that options in my case companyIndustryTypes is an array of object :

[
    {
        id: 1,
        name: "Accounting",
    },
    {
        id: 2,
        name: "Administration & Office Support",
    },
    ...
]

enter image description here

Upvotes: 6

hotcakedev
hotcakedev

Reputation: 2515

First, you could use the Autocomplete component without inputValue and OnInputValue props.

      ...
      <Autocomplete
        multiple
        value={value}
        onChange={(event: any, newValue: any | null) => {
          //setValue(newValue);
        }}
        id="controllable-states-demo"
        options={options}
        sx={{ width: 300 }}
        renderInput={(params) => <TextField {...params} label="Controllable" />}
      />

But it won't work for the selection, only search will work.

Second, if you want its search as well as selection to work, then you should use more a couple of Autocomplete props.

...
export default function ControllableStates() {
  const [value, setValue] = React.useState<any | null>([]);
  // you need to set the selected value your own
  // const value = [];
  const [inputValue, setInputValue] = React.useState("");

  console.log("Current Value:", value);

  return (
    <div>
      <div>{`value: ${value !== null ? `'${value}'` : "null"}`}</div>
      <div>{`inputValue: '${inputValue}'`}</div>
      <br />
      <Autocomplete
        multiple
        value={value}
        onChange={(event: any, newValue: any | null) => {
          setValue(newValue.map(option => option.value || option));
        }}
        isOptionEqualToValue={(option, value) => option.value === value}
        getOptionLabel={(option) => {
          if (typeof option === 'number') {
            return options.find(item => item.value === option)?.label;
          } else {
            return option.label;
          }
        }}
        id="controllable-states-demo"
        options={options}
        sx={{ width: 300 }}
        renderInput={(params) => <TextField {...params} label="Controllable" />}
      />
    </div>
  );
}

As you can see it doesn't need to use the inputValue and onInputChange props as well.

Please make sure if you match the correct types of the selected value and option.

Upvotes: 2

Related Questions