How to use the Material Ui autocomplete Component without errors

Asked

Viewed 1,497 times

0

I have a component Autocomplete of the Material UI where the user searches the values based on an array of products that comes from an API. The component is like this, where products is the array, setPointsProduct and setItem is my state. :

<Autocomplete
            value={item}
            id="autocomplete"
            onChange={(event, newValue) => {
              if (newValue) {
                const values = newValue.split('-');
                setPointsProduct(values[1]);
                setItem(values[0]);
                document.getElementById('quantity').focus();
              } else {
                toast.error('Selecione um produto');
              }
            }}
            options={products.map(
              product => `${product.name} - ${product.points}`
            )}
            renderInput={params => (
              <TextField
                {...params}
                label="Produtos"
                required
                className={classes.points}
                autoFocus
                variant="outlined"
              />
            )}
          />
  

The component works but every time I open the console this error appears:

useAutocomplete.js:249 Material-UI: The value provided to Autocomplete is invalid.
None of the options match with `""`.
You can use the `getOptionSelected` prop to customize the equality test.

How can I withdraw this error? I am doing something wrong in the component?

2 answers

3


Fact

The message is saying that it has a value provided in the property value that is not present in the list of options.

Motive

The component Autocomplete is searching in your list of options a value that is not present.

I imagine we have the following list of products.

const products = [
  { name: "Arroz", points: 100 },
  { name: "Feijão", points: 120 },
  { name: "Açucar", points: 80 },
  { name: "Beterraba", points: 32 },
  { name: "Alface", points: 5 },
  { name: "Carne", points: 30 },
  { name: "Chicoria", points: 20 },
  { name: "Leite", points: 12 }
];

Autocomplete options according to your code:

options={products.map(product => `${product.name} - ${product.points}`)}

Will be:

const opcoes = [
  'Arroz - 100',
  'Feijão - 120',
  'Açucar - 80',
  'Beterraba - 32',
  'Alface - 5',
  'Carne - 30',
  'Chicoria - 20',
  'Leite - 12',
]

By doing the setItem(values[0]) you are saying the value (property value) will be only the name of your product, and not the full value you provided in the options, which is composed by nome - pontos. In that case the Autocomplete will not be able to determine the equality of the values among the selected nomeApenas and value nome - pontos.

Solution

Following what you demonstrated, we could fix with the property getOptionSelected.
This property is used when you need to determine a different way of testing the equality between the property value value and your list of options.
It is a function that takes two values, the first is the option and the second is the value. This way you can apply the same rule you use to set the value.

getOptionSelected={(option, value) => {
  const values = option.split("-");
  return values[0] === value;
}}

Here follows an implementation of the applied solution.

Edit Button

Recommending

I don’t know what the real use is. But I recommend that you use the value with the object itself contained in the array. Use the properties getOptionSelected and getOptionLabel to control what is selected and what is displayed.
Here is an implementation using these two properties controlling selection and visualization.

Edit Button

I hope it helps.

0

I had the same problem and after reading Jairon’s solution, I was able to solve it. I share below the final result, with parameterization that allows the component to be used for several different contents:

import React, { ChangeEvent } from 'react';
import TextField from '@material-ui/core/TextField';
import Autocomplete, { AutocompleteChangeReason, AutocompleteChangeDetails } from '@material-ui/lab/Autocomplete';
import { makeStyles, Theme, createStyles } from '@material-ui/core';

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    campo: {
      margin: "2% 2%",
      width: "98%"
    },
  })
);

type AutoProps=
{
  nome: string,
  label: string,
  classe: string,
  valor: any,
  habilitado: boolean,
  conteudo: any [],
  colunas: {id: string, desc: string}
  onchange: ((event: ChangeEvent<{}>, value: any, reason: AutocompleteChangeReason,  details?: AutocompleteChangeDetails<any> | undefined, campo?: string) => void)
}


export default function SelecaoAuto({nome, label, classe, valor, conteudo, habilitado, colunas, onchange}: AutoProps) {

    const classes=useStyles();

    return (
        <Autocomplete
            className={classe ? classe : classes.campo}
            id={nome}
            options={conteudo}
            getOptionLabel={cliente => cliente[colunas.desc]}
            getOptionSelected={(option: any, value: any) => option[colunas.id] === value[colunas.id]}
            onChange={(event, value, reason, details) => onchange(event, value, reason, details, nome)}
            disabled={!habilitado}
            value={valor}
            renderInput={(params) => <TextField {...params} label={label} placeholder={label} variant="outlined" />}
        />
    );
}

In the onchange function on the component that uses the above component, you can set the state (I chose not to use the state on the custom component).

Example:

    const onChangeRecebimentosAutoComplete = (event: ChangeEvent<{}>, value: any, reason: AutocompleteChangeReason, details?: AutocompleteChangeDetails<any> | undefined, campo?: string) =>
    {
typeof reason, details, typeof details)
      if(campo)
      {
        if (value[campo])
        {
          setRecebimento({...recebimento, [campo]: value[campo]});
          console.log('setrecebimnento: ', recebimento.id_cli, value[campo], campo);
        }
      }
    }

And in the component call, I also use a call to a function to identify the array item passed as value:

    const RetornaCliente = (idcli: number) =>
    {
        console.log('retorna cliente: ', idcli);
        let clesc = undefined;
        let indice=clientes.findIndex((cliente:any) => cliente.id_cli===idcli)
        if(indice>=0)
        {
            clesc=clientes[indice];
        }
        console.log('retorna cliente fim:',clesc);
        return clesc;
    }


<SelecaoAuto valor={RetornaCliente(recebimento.id_cli)} nome='id_cli' label='Cliente' classe={classes.textoform} conteudo={clientes} habilitado={true} colunas={{id: 'id_cli', desc: 'nome_cli'}} onchange={onChangeRecebimentosAutoComplete}/>

Browser other questions tagged

You are not signed in. Login or sign up in order to post.