mui / material-ui

Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
https://mui.com/material-ui/
MIT License
94.13k stars 32.35k forks source link

[Autocomplete] The value provided to Autocomplete is invalid. (with the installed isOptionEqualToValue) #29727

Closed nihil-pro closed 3 months ago

nihil-pro commented 3 years ago

Duplicates

Latest version

Current behavior 😯

I get an «value provided to Autocomplete is invalid» error:

None of the options match with `{"id":18877,"YearFrom":2002,"YearTo":2005,"Make":"Hyundai","Model":"Getz","Generation":"I (2002—2005)","FuelType":"Бензин","DriveType":"Передний","Transmission":"Автомат","Modification":"1.4 AT (97 л.с.)","Power":97,"EngineSize":"1.4","BodyType":"Хетчбэк","Doors":"5"}`.
You can use the `isOptionEqualToValue` prop to customize the equality test.

Expected behavior 🤔

Well, if i use the isOptionEqualToValue={(option, value) => option.id === value.id}, the expected behavior is that I shouldn't get an error.

Steps to reproduce 🕹

Steps:

  1. Use Autocomplete:
<Autocomplete
        multiple
        fullWidth
        filterSelectedOptions
        defaultValue={defaultValues}
        options={options}
        isOptionEqualToValue={(option, value) => option.id === value.id}
        getOptionLabel={(option: ICar) =>
          `(${option.id}) 
          ${option.Make} ${option.Model} ${option.Generation} 
          ${option.EngineSize} л ${option.FuelType?.toLowerCase()} ${option.Power} л.с. – ${option.Transmission?.toLowerCase()} ${option.DriveType?.toLowerCase()} привод
          `
        }
        renderInput={(params: AutocompleteRenderInputParams) => <TextField {...params} label={'Применяемость'} />}
        renderOption={(props, option: ICar) => {
          return (
            <li {...props}>
              {option.Make} {option.Model} {option.Generation} {option.EngineSize} л {option.FuelType?.toLowerCase()} {option.Power} л.с. – {option.Transmission?.toLowerCase()} {option.DriveType?.toLowerCase()} привод
            </li>
          )
        }}
        onChange={(_, value) => onChange(value)}
        onInputChange={(event, value) => {
          if (value && value.length >= 3) {
            search(value).finally()
          }
        }}
 />
  1. The defaultValue is also a type Array
  2. I get an error with defaulValue options only

Context 🔦

No response

Your environment 🌎

`npx @mui/envinfo` ``` System: OS: macOS 12.0.1 Binaries: Node: 16.9.1 - /usr/local/bin/node Yarn: 1.22.4 - /usr/local/bin/yarn npm: 6.14.6 - ~/node_modules/.bin/npm Browsers: Chrome: 96.0.4664.45 Edge: Not Found Firefox: 94.0.1 Safari: 15.1 npmPackages: @emotion/react: ^11.4.1 => 11.4.1 @emotion/styled: ^11.3.0 => 11.3.0 @mui/core: 5.0.0-alpha.50 @mui/icons-material: ^5.0.3 => 5.0.3 @mui/material: ^5.0.3 => 5.0.3 @mui/private-theming: 5.0.1 @mui/styled-engine: 5.0.1 @mui/styles: 5.0.1 @mui/system: 5.0.3 @mui/types: 7.0.0 @mui/utils: 5.0.1 @mui/x-data-grid: ^5.0.0-beta.3 => 5.0.0-beta.3 @types/react: ^17.0.20 => 17.0.20 react: ^17.0.2 => 17.0.2 react-dom: ^17.0.2 => 17.0.2 typescript: ^4.4.3 => 4.4.3 ```
michaldudak commented 3 years ago

Could you please create a codesandbox highlighting the problem? You can use the following template: https://mui.com/r/issue-template

nihil-pro commented 3 years ago

Could you please create a codesandbox highlighting the problem? You can use the following template: https://mui.com/r/issue-template

Sure https://codesandbox.io/embed/boring-night-8jcvx?fontsize=14&hidenavigation=1&theme=dark

Also another problem, if i set the startAdornment like an icon, then the selected options are not shown

michaldudak commented 2 years ago

Sorry for the late response. I'm getting a 404 error on the sandbox you provided. Could you check it?

markedwards commented 2 years ago

I have this issue as well, and I think its the same case. It can happen when implementing an external async search. The Autocomplete component still has a value, but the options are now populated with the results from the search, which does not include the current value.

Here is a reproduction: https://codesandbox.io/s/asynchronous-material-demo-forked-q0yox?file=/demo.tsx

Steps to repro:

  1. type "inter" in the Autocomplete, chose "Interstellar"
  2. deselect the Autocomplete
  3. select the Autocomplete, type "godfather"
  4. observe console

It doesn't repro 100% of the time, but its pretty easy to get it to happen.

markedwards commented 2 years ago

@mbrookes can this be reopened?

michaldudak commented 2 years ago

Thanks for the reproduction, @markedwards. I'm reopening the issue.

tumetus commented 2 years ago

I'm experiencing this problem too. Great to see that this issue is opened!

mcsimps2 commented 2 years ago

Also ran into this issue while fetching autocomplete options from the Google Maps Places API and my own backend. I tried to work around the issue by clearing out the value prop when the inputValue changed since any change in inputValue changes the options prop with new API results. This makes the warning go away but results in a clunky user behavior where it takes 2 keystrokes to start a new "search" instead of one. For some reason, the first one keystroke disappears into oblivion. (E.g. if you highlight the text and then try to replace it by typing, your first key stroke only clears the text and doesn't place any actual letters into the TextField.) It sounds like some order of operations problem so I didn't poke in further.

Open to suggestions for other ways to avoid the warning or if it's okay to just to ignore it overall.

bsides commented 2 years ago

Could we please get an option to turn off the warning completely? At this stage it's causing a lot of noise in tests and, as reported, it's not warning anything useful.

bbauer02 commented 2 years ago

I have the same Issue ... boring warning

michaldudak commented 2 years ago

Apparently, at this point, the warning causes more confusion than assistance. If anyone is willing to investigate if this warning can be disabled for cases of async loading without much hassle, then feel free to grab this issue.

I'm also considering removing the warning altogether, but let's try the less destructive option first.

angrybacon commented 2 years ago

In case anyone stumbles upon this issue today still, the trick for async-populated options is to add the currently selected option so that it doesn't trigger the false positive that can happen when setting a new value while an async request changes the array of options that is provided.

const [item, setItem] = useState<ItemModel>();
const [items, setItems] = useState<ItemModel[]>([]);
const [input, setInput] = useState('');
const query = useDebounce(input, 250);

useEffect(() => {
  fetchItems(query).then((items) => setItems(items));
}, [query]);

return (
  <Autocomplete
    autoComplete
    filterSelectedOptions
    getOptionLabel={(option) => option.name}
    onInputChange={(_event, value) => setInput(value)}
    onChange={(_event, value) => setItem(value)}
    options={item ? [item, ...items] : items}
    renderInput={(extra) => <TextField {...extra} label="Name" />}
  />
);
bryanltobing commented 2 years ago

still an issue for me. not wanting to add more state as said in @angrybacon answer, because i use react hook form mainly to control the state.

 <Controller
  name={"district"}
  control={control}
  render={({ field: { onChange, value } }) => (
    <Autocomplete
      onChange={(e, data) => onChange(data)}
      options={districts.map(({ id, name }) => ({ id, name }))}
      value={value}
      getOptionLabel={(option) => option.name}
      isOptionEqualToValue={(option, newValue) => {
        return option.id === newValue.id;
      }}
      renderInput={(params) => (
        <TextField {...params} placeholder="test" label="test" />
      )}
      onInputChange={(_evt, newValue) => setSearch(newValue)}
      inputValue={search}
    />
  )}
/>

can i just return true for isOptionEqualToValue prop? is it affect anything., didn't find the effect so far. even still don't get it the purpose of that prop in the first place

ianpaschal commented 2 years ago

I'm currently struggling with this. I'm going to use this comment box as a rubber duck and maybe someone can explain what I'm doing wrong.

So... This error has made me realize I don't entirely get what the point of the value field is anyway. As I understand it, the autocomplete component is essentially a text input and a select welded together. The onChange handler fires when the user clicks an item à la select, whereas onInputChange and inputValue are for the text input portion.

From what I can see, the both the text input and the selection set the value, which makes sense because while there's only one value for the component, even if there's two ways to set it.

I've been storing my value in a hook, and passing it back in to both value and inputValue and getting this warning (even though everything works fine). I figured it's because value expects some item in my options array (such as an {label: string; id: string;}). So I tried storing the value separately (as the docs do indeed suggest), but that:

  1. Does not suppress the warning (it just complains now that the object doesn't match either).
  2. Causes the typed input to be over-written by the last select value.

The latter I suppose can be mitigated by unsetting the "select value" when the user modifies the input text and falling back to value={inputValue} but that raises the question, "Why set it to anything besides inputValue in the first place?"

So, am I doing something wrong or is this warning truly just meaningless? I feel like I must be doing something wrong because surely the value prop is not as useless as it appears to be?

I think for now I will just use a <Select /> instead of <Autocomplete />. I'll miss the nice filtering/instant search but I don't really want a component like this where I can't figure out from the documentation how it's supposed to be used and which spews out console warnings in my production code base. 😞

As a closing note, if my intuitions are correct, I'd argue the naming of the Autocomplete props is rather misleading/confusing. The text field value should be called value and have its onChange property, and there should be another prop called onSelect or 'onComplete` which is used for when the value is set by via autocomplete behavior.

Thoughts?

ianpaschal commented 2 years ago

I forked the controlled input example from the documentation and tweaked it slightly, working towards what I was actually building (albeit not for Harry Potter characters).

Although the warning does not show up, there is a lot of questionable UX going on. You will notice the behavior where you delete half the name and it reappears on blur because of the odd implementation choice of having the Autocomplete have two values.

I've "solved" that by setting value via the onInputChange handler, which removes the need for two pieces of state, and removes the weird ambiguity between the two competing/conflicting values. But the documentation explicitly advises against this.

Of course, creating this dummy value object does make it clear why the warning shows up.

Which would bring me back to: Either the Autocomplete component should be completely re-thought (probably the "right" solution) w.r.t. multiple values and setters, OR the warning should be ditched and you can set the value to whatever you want.

The latter also does feel like a "right" choice considering entering non-specific options is kind of the whole thing that sets <Autocomplete /> apart from <Select />. If you don't want the user to be able to type in random other text, use a <Select />, no?

LeVraiNinjaneer commented 2 years ago

Got same issue. Too bad there's no solution yet, but glad that it's on the radar.

shinsou commented 2 years ago

I had a bit different kind of a problem that is described in this thread, but as it lands on same field I decided to post here rather to post new thread (hope that's fine).

TL;DR

A opinion that has very minor affect on... anything. May resolve some common issues found in internet with this component.

Decription

I have MUIv5 and I use MUI Autocomplete, with Formik (with Yup) and use typescript. I've come into an opinion where I feel like that useAutocomplete (@mui/base/AutocompleteUnstyled/useAutocomplete.js) applies bad practices.

By the snipped code below, it states that the Autocomplete initial value can not be undefined, but rather null? -- henche, to my knowledge undefined is stated as "not set" or in validation schemes "not touched" and null is actually a defined value ("someone or something have changed or set it's value to null).

In my case this caused the form to be initialized with "undefined"... string (what the ...) and bunch of error/warning messages, saying:

  if (process.env.NODE_ENV !== 'production') {
    if (value !== null && !freeSolo && options.length > 0) { // <-----------
      const missingValue = (multiple ? value : [value]).filter(value2 => !options.some(option => isOptionEqualToValue(option, value2)));

      if (missingValue.length > 0) {
        console.warn([`MUI: The value provided to ${componentName} is invalid.`, `None of the options match with \`${missingValue.length > 1 ? JSON.stringify(missingValue) : JSON.stringify(missingValue[0])}\`.`, 'You can use the `isOptionEqualToValue` prop to customize the equality test.'].join('\n'));
      }
    }
  }

Curtain call

I resolved my problem by initializing form values with null and applying nullable into Yup validation schema for the fields.

Sample code:

<Field
    name="fieldName"
    component={Autocomplete}
    options={sortedfieldNameKeys}
//    getOptionLabel={(option: string) => sortedfieldNameKeys?.length > 0 ? fields[option] : "" }
    getOptionLabel={(option: string) => sortedfieldNameKeys?.length > 0 ? fields[option] : null }
    renderInput={(params: AutocompleteRenderInputParams) => (
        <MuiTextField
            {...params}
            name="fieldName"
            label={translator('fieldName')}
            variant="standard"
            helperText={touched?.fieldName && errors.fieldName && errors.fieldName}
            error={touched?.fieldName && Boolean(errors.fieldName)}
        />
    )}
    sx={{ width: '20ch', mr: 4 }}
/>
// ....
=> fields = await getFields(); // [ { "01aeed68-...", value: "Field 1" }, { "470401eb-...", value: "Field 2" },... ]
// ....
=> sortedFieldNameKeys = Object.entries(fields).map(({key, value}) => (key));
// ....
=> form = {
  ...,
  fieldName: null, // '' // undefined  ... oof
}
parulraheja98 commented 2 years ago

Is there any update on this? Still able to reproduce this issue locally. @michaldudak

monika-parmar commented 2 years ago

I'm facing this warning everytime when I try to select a link, that I've provided in dropdown, with the help of paper component. I'm sharing the Autocomplete component and the UI screenshot of the dropdown <Autocomplete popupIcon={''} fullWidth clearOnBlur clearOnEscape sx={[ error.fullAddress ? commonClasses.fieldError : {}, onboardingStyle.searchAddressAutocomplete, ]} loading={isOnboardingFormAddressLookUpSearchLoaderActive} disableClearable={!!searchAddressValue} value={onboardingForm.fullAddress} data-testid={ON_BOARDING_TEST_CONSTANTS.ADDRESS} options={searchAddressResults} PaperComponent={(paperComponentProps) => ( <SearchAddressDropdown paperComponentProps={paperComponentProps} onboardingForm={onboardingForm} setOnboardingForm={setOnboardingForm} setIsAddressModalOpen={setIsAddressModalOpen} /> )} noOptionsText={ searchAddressValue?.length > 2 ? 'No search results found' : 'Search address..' } renderInput={(params) => ( <TextField onChange={debounceSearch} {...params} label='ADDRESS' InputProps={{ ...params.InputProps, type: 'search', }} helperText={error?.fullAddress} /> )} onChange={onOptionChange} />

image image
tomkingkong commented 2 years ago

For anyone who may still be running into this warning. The issue I ran into was a mislabelling of "freeSolo", with a capital "S".

jjrmunoz commented 2 years ago

I have got the same issue - getting the warning: "The value provided to Autocomplete is invalid. None of the options match with "". You can use the isOptionEqualToValue prop to customize the equality test".

Adding the final part || value === "" to isOptionEqualToValue={(option, value) => option.name === value.name || value === "" removes the warning, but then a new problem appears: all options turn colored as if all were chosen

vuhi commented 2 years ago

I have multi select AutoComplete. This is the way I solve the problem:

derekcannon commented 1 year ago

While freesolo may work to solve this issue, it seems like a workaround for a common use case. Using multi-select with server-side search is a common pattern that shouldn't trigger a warning.

Perhaps there should be a prop to signify "Search as you type" (as the Docs refers to it) that does not allow for free entry but does allow for items to exist in the values that don't exist in the current options and, by default, disables filterOptions (instead of having to set filterOptions={(x) => x} as the Docs recommends).

grzegorz-jazurek commented 1 year ago

@michaldudak I added PR with disableOptionEqualToValueWarning prop which disables that warning. I would appreciate any suggestions if you guys have any ideas about naming. The freeSolo attribute modifies the standard behavior of Autocomplete component that's why it was not a solution in my case

minchaej commented 1 year ago

Hi all. Is there an update on this issue? I agree 200% with @derekcannon , as I am using multi-select autocomplete with queried options upon keyboard input. So the selected options may not be on the current options, as the options are queried upon input and dynamic. Everything works fine, but I would love to suppress the Warning... disableOptionEqualToValueWarning <-- looks sweet to me! Thanks! @grzegorz-jazurek

marinamsou commented 1 year ago

Any updates on this? Looks like this suggestion could allow fixing the issue.

michaldudak commented 1 year ago

I can't say I like the idea of introducing yet another prop to the Autocomplete, especially just for suppressing a warning. @DiegoAndai, @mj12albert, @mnajdova, I propose to remove the warning altogether.

DiegoAndai commented 1 year ago

@michaldudak I agree that we should suppress the warning. We could add a warning in the docs that covers this (https://github.com/mui/material-ui/issues/18514#issuecomment-557880360)

criscola commented 1 year ago

I agree with suppressing this warning. In my use case, I have one HTTP GET request to get my full object, and then map each value to the form as needed (as a view form, that can be edited). The Autocomplete is populated when the user types, in this case I have to handle it to avoid the warning, but it wouldn't really be necessary, options will be populated as soon as the user interacts with the autocomplete (including the initial option), so in summary it would be nice to not deal with the warning at all for these cases.

DiegoAndai commented 1 year ago

We agree regarding suppressing the warning and adding a warning in the docs, so I'll add the ready to take label. If anyone starts working on this, let us know.

cc: @mnajdova

Sboonny commented 11 months ago

I am new to the repo, I would love to contribute, and I will read the contribution.md.

Just wondering, is to surppress the warning is to delete the console.warning() and update related tests?

Edit: nvm, I just noticed the open pr

tyforcode commented 8 months ago

I came across this as I was trying to resolve this issue myself. I was able to get this warning removed by providing a null fallback for value:

const value = field.value || null;
<Autocomplete
  value={value}
  ...
/>

I'm still on MUI 5 but wanted to share in case this works for the latest versions

FahadAminShovon commented 8 months ago

I was able to fix the issue following the trail of the warning

first let's find out what it does

if you use the isOptionEqualToValue properly it highlights the selected option in the options list

Screenshot 2024-03-24 at 11 05 30 PM

how does it do it? It does it by comparing the keys or objects internally(probably).

if you don't , the selected option won't get highlighted

Screenshot 2024-03-24 at 11 09 23 PM

and you get this error

MUI: The value provided to Autocomplete is invalid.
None of the options match with `{"label":"Option 2","value":"2"}`.
You can use the `isOptionEqualToValue` prop to customize the equality test.

if you set the freeSolo property to true it skips the check which I don't recommend because I don't know which value is it changing internally

what worked for me is setting the default value to null when I don't have any value

don't set default value to undefined if you are using it as controlled as it'll result in controlled -> uncontrolled warning

here is my implementation with react hook form

import { Autocomplete, TextField } from "@mui/material";
import { Controller, useForm } from "react-hook-form";

type OptionType = {
  label: string;
  value: string;
};

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

type FormType = {
  selectedOption: OptionType | null;
};

export const DemoComponent = () => {
  const { control } = useForm<FormType>({
    defaultValues: { selectedOption: null },
  });

  return (
    <Controller
      name={"selectedOption"}
      control={control}
      render={({ field: { value, onBlur, onChange } }) => (
        <Autocomplete
          options={options}
          value={value}
          getOptionLabel={(option) => option.label}
          fullWidth
          isOptionEqualToValue={(option, selectedValue) => {
            return option.value === selectedValue.value;
          }}
          onChange={(_, val) => {
            onChange(val);
          }}
          renderInput={(params) => (
            <TextField
              {...params}
              onBlur={onBlur}
              label={"Select an option"}
              placeholder={"Select an option"}
              InputProps={{
                ...params.InputProps,
              }}
            />
          )}
        />
      )}
    />
  );
};
Eduardo-Miguel commented 7 months ago

I am using typescript and this had worked for me:

<Autocomplete
  //freeSolo
  id="size-small-outlined"
  size="small"
  className="flex-1"
  options={seletorLoteOrcamento}
  isOptionEqualToValue={(option, selectedValue) => option.value === selectedValue.value}  // <- you also need to add this code
  onChange={(event, newValue) => {
    const labelValueSelecionado = newValue as ILabelValue; // <- I just typed the value and it worked
    lidarOrcamentoSelecionado(Number(labelValueSelecionado?.value));
  }}
  renderInput={(params) => <TextField {...params} label="Orçamento" />}
/>

I hope it works for you too.

tisonv commented 7 months ago

Any updates ?

I'm encountering the same issue with latest Mui even without Multiple activated :

  1. I select a value => ok
  2. I select a new value (because I chose the wrong one) => warning
DiegoAndai commented 7 months ago

@mnajdova given that this issue is getting activity consistently, I think we should remove the warning for v6. What do you think?

KarineBrandelli commented 4 months ago

I have multi select AutoComplete. This is the way I solve the problem:

  • options: <[{ label, value, type }]>
  • Turn on freeSolo, autoSelect to ignore warning
  • Turn on clearOnBlur to ignore value that not [Enter] or throw selecting an option in dropdown
  • Modify onChange to handle unwanted text
<Autocomplete
      freeSolo
      disabled={loading}
      multiple
      clearOnBlur
      options={options}
      getOptionLabel={(option) => option.label}
      groupBy={(option) => option.type}
      /** Help show no option dropdown, freeSolo does not show it **/
      getOptionDisabled={(option) => option.label && option.label.toLowerCase() === 'no option'}
      filterOptions={() => {
        if (!options.length) return [{ label: 'No option', value: 'No option' }];
        return options;
      }}
      value={selectedOptions}
      onChange={(event, selectedValues) => {
          // This only apply to multi select
          if (['removeOption', 'clear'].includes(reason)) {
              setSelectedOptions(selectedValues);
          }
          if (['selectOption'].includes(reason)) {
              const lastValue = [...multiValue].pop();
              const isAllow = options.some((item) => item.label === lastValue?.label);
              if (!isAllow) break;
              setSelectedOptions(multiValue);
          }
      }}
      onInputChange={(event, value) => setSearchTerm(value)}
      renderInput={(params) => (
          <TextField {...params} label="label" />
      )}
/>

Happy coding :)

saved my life! ❤️

ZeeshanTamboli commented 3 months ago

Created PR to remove the warning: #43314

RafaelAngeloPiad commented 2 months ago

I have multi select AutoComplete. This is the way I solve the problem:

  • options: <[{ label, value, type }]>
  • Turn on freeSolo, autoSelect to ignore warning
  • Turn on clearOnBlur to ignore value that not [Enter] or throw selecting an option in dropdown
  • Modify onChange to handle unwanted text
<Autocomplete
      freeSolo
      disabled={loading}
      multiple
      clearOnBlur
      options={options}
      getOptionLabel={(option) => option.label}
      groupBy={(option) => option.type}
      /** Help show no option dropdown, freeSolo does not show it **/
      getOptionDisabled={(option) => option.label && option.label.toLowerCase() === 'no option'}
      filterOptions={() => {
        if (!options.length) return [{ label: 'No option', value: 'No option' }];
        return options;
      }}
      value={selectedOptions}
      onChange={(event, selectedValues) => {
          // This only apply to multi select
          if (['removeOption', 'clear'].includes(reason)) {
              setSelectedOptions(selectedValues);
          }
          if (['selectOption'].includes(reason)) {
              const lastValue = [...multiValue].pop();
              const isAllow = options.some((item) => item.label === lastValue?.label);
              if (!isAllow) break;
              setSelectedOptions(multiValue);
          }
      }}
      onInputChange={(event, value) => setSearchTerm(value)}
      renderInput={(params) => (
          <TextField {...params} label="label" />
      )}
/>

Happy coding :)

saved my life! ❤️

SAVED MY LIVE TOO