'Handle change on Autocomplete Component from material ui

I want to use Autocomplete component for input tags. I'm trying to get the tags and save them on a state so I can later save them on the database. I'm using functions instead of classes in react. I did try with onChange, but I didn't get any result.

<div style={{ width: 500 }}>
  <Autocomplete
    multiple
    options={autoComplete}
    filterSelectedOptions
    getOptionLabel={(option) => option.tags}
    renderInput={(params) => (
      <TextField
        className={classes.input}
        {...params}
        variant="outlined"
        placeholder="Favorites"
        margin="normal"
        fullWidth
      />
    )}
  />
</div>;


Solution 1:[1]

As Yuki already mentioned, make sure you did use the onChange function properly. It receives two parameters. According to the documentation:

Signature: function(event: object, value: any) => void.

event: The event source of the callback

value: null (The value/values within the Autocomplete component).

Here's an example:

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

export default class Tags extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tags: []
    };
    this.onTagsChange = this.onTagsChange.bind(this);
  }

  onTagsChange = (event, values) => {
    this.setState({
      tags: values
    }, () => {
      // This will output an array of objects
      // given by Autocompelte options property.
      console.log(this.state.tags);
    });
  }

  render() {
    return (
      <div style={{ width: 500 }}>
        <Autocomplete
          multiple
          options={top100Films}
          getOptionLabel={option => option.title}
          defaultValue={[top100Films[13]]}
          onChange={this.onTagsChange}
          renderInput={params => (
            <TextField
              {...params}
              variant="standard"
              label="Multiple values"
              placeholder="Favorites"
              margin="normal"
              fullWidth
            />
          )}
        />
      </div>
    );
  }
}

const top100Films = [
  { title: 'The Shawshank Redemption', year: 1994 },
  { title: 'The Godfather', year: 1972 },
  { title: 'The Godfather: Part II', year: 1974 },
  { title: 'The Dark Knight', year: 2008 },
  { title: '12 Angry Men', year: 1957 },
  { title: "Schindler's List", year: 1993 },
  { title: 'Pulp Fiction', year: 1994 },
  { title: 'The Lord of the Rings: The Return of the King', year: 2003 },
  { title: 'The Good, the Bad and the Ugly', year: 1966 },
  { title: 'Fight Club', year: 1999 },
  { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 },
  { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 },
  { title: 'Forrest Gump', year: 1994 },
  { title: 'Inception', year: 2010 },
];

Solution 2:[2]

I needed to hit my api on every input change to get my tags from backend!

Use Material-ui onInputChange if you want to get your suggested tags on every input change!

this.state = {
  // labels are temp, will change every time on auto complete
  labels: [],
  // these are the ones which will be send with content
  selectedTags: [],
}
}

//to get the value on every input change
onInputChange(event,value){
console.log(value)
//response from api
.then((res) => {
      this.setState({
        labels: res
      })
    })

}

//to select input tags
onSelectTag(e, value) {
this.setState({
  selectedTags: value
})
}


            <Autocomplete
            multiple
            options={top100Films}
            getOptionLabel={option => option.title}
            onChange={this.onSelectTag} // click on the show tags
            onInputChange={this.onInputChange} //** on every input change hitting my api**
            filterSelectedOptions
            renderInput={(params) => (
              <TextField
                {...params}
                variant="standard"
                label="Multiple values"
                placeholder="Favorites"
                margin="normal"
                fullWidth
              />

Solution 3:[3]

Are you sure you used onChange correctly?

onChange signature: function(event: object, value: any) => void

Solution 4:[4]

I wanted to update my state when I select an option from the autocomplete. I had a global onChange handler that manages for all inputs

         const {name, value } = event.target;
         setTukio({
          ...tukio,
          [name]: value,
        });

That updates the object dynamically based on the name of the field. But on the Autocomplete the name returns blank. So I changed the handler from onChange to onSelect. Then either create a separate function to handle the change or as in my case added an if statement to check if the name is not passed.

// This one will set state for my onSelect handler of the autocomplete 
     if (!name) {
      setTukio({
        ...tukio,
        tags: value,
      });
     } else {
      setTukio({
        ...tukio,
        [name]: value,
      });
    }

The above approach works if u have a single autocomplete. If you have multiple u can pass a custom function like below

<Autocomplete
    options={tags}
    getOptionLabel={option => option.tagName}
    id="tags"
    name="tags"
    autoComplete
    includeInputInList
    onSelect={(event) => handleTag(event, 'tags')}
          renderInput={(params) => <TextField {...params} hint="koo, ndama nyonya" label="Tags" margin="normal" />}
        />

// The handler 
const handleTag = ({ target }, fieldName) => {
    const { value } = target;
    switch (fieldName) {
      case 'tags':
        console.log('Value ',  value)
        // Do your stuff here
        break;
      default:
    }
  };

Solution 5:[5]

@Dworo

For anyone that has a problem with displaying a selected item from the dropdown in the Input field.

I found a workaround. Basically, you have to bind an inputValue at onChage for both Autocomplete and TextField.

const [input, setInput] = useState('');

<Autocomplete
  options={suggestions}
  getOptionLabel={(option) => option}
  inputValue={input}
  onChange={(e,v) => setInput(v)}
  style={{ width: 300 }}
  renderInput={(params) => (
    <TextField {...params} label="Combo box" onChange={({ target }) => setInput(target.value)} variant="outlined" fullWidth />
  )}
/>

Solution 6:[6]

This worked for me:

I have an array of objects of type:

{
   id: someId,
   label: someLabel,
}

let's call this array "items". And use a formState object, like this:

   const [formState, setFormState] = useState({
      idPuntoVenta: '',
      nomPuntoVenta: '',
      selected: items[0],
    });

And defined the Autocomplete component like this:

<Autocomplete
    disablePortal
    id="salesPoint"
    options={sortedSalesPoints}
    value={selected}
    onChange={(event, newValue) => {
        setFormState({
            ...formState,
            selected: newValue,
            idPuntoVenta: newValue?.id,
        });
    }}
    inputValue={nomPuntoVenta}
    onInputChange={(event, newInputValue) => {
        setFormState({
            ...formState,
            nomPuntoVenta: newInputValue,
        });
    }}
    renderInput={(params) => (
        <TextField
            {...params}
            className="form-control"
            size="small"
            label="Punto de venta"
            required
        />
    )}
/>;

The important thing here (and the one that took me a while) is to understand that the {value} property of the Autocomplete component is an object of the same type of the ones stored in the array "items"

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2
Solution 3 Yuki
Solution 4 TRANS
Solution 5
Solution 6 Ricardo Álvarez