Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Autocomplete] Warn when value and option doesn't match #18514

Closed
guimex22 opened this issue Nov 23, 2019 · 51 comments · Fixed by #20235
Closed

[Autocomplete] Warn when value and option doesn't match #18514

guimex22 opened this issue Nov 23, 2019 · 51 comments · Fixed by #20235
Labels
component: autocomplete This is the name of the generic UI component, not the React module! docs Improvements or additions to the documentation good first issue Great for first contributions. Enable to learn the contribution process.

Comments

@guimex22
Copy link

Hi,
I am trying to change list options and default value from state. List option is ok but default value is never update.

I have add an exemple on this code box :
https://codesandbox.io/s/test-material-ui-autocomplete-700nh?fontsize=14&hidenavigation=1&theme=dark

Thank you for yours ideas...

@oliviertassinari oliviertassinari added the support: Stack Overflow Please ask the community on Stack Overflow label Nov 23, 2019
@support

This comment has been minimized.

@support support bot closed this as completed Nov 23, 2019
@oliviertassinari oliviertassinari added the duplicate This issue or pull request already exists label Nov 23, 2019
@oliviertassinari
Copy link
Member

oliviertassinari commented Nov 23, 2019

Control the component, we don't aim to support defaultValue prop updates (even if a native input do).

@guimex22
Copy link
Author

Thank you for your reply. I am newbie andI think dificulty come from here... but when I add state variable on autocomplete value, items are show on textarea but not selected on dropdown list (and I can selected them second time).
Do you have a sample of controlled used of autocomplete ?

@oliviertassinari
Copy link
Member

You can find one controlled demo in https://material-ui.com/components/autocomplete/#playground.

@guimex22
Copy link
Author

I have see this but when I try to manage values directely, dropdownlist is not updated with selected values passed by state as you can see on my exemple :
https://codesandbox.io/s/test-material-ui-autocomplete-o3uic?fontsize=14&hidenavigation=1&theme=dark

Push button change list and add item preselected but in dropdown list this item is not selected and we can add it another time.

@oliviertassinari
Copy link
Member

oliviertassinari commented Nov 24, 2019

@guimex22 The value needs to be referentially equal to the option to consider it selected. We have #18443 to customize this.

However, I think that we have an opportunity to warn about this case. What do you think of this diff?

diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
index f833a0c0c..5cd34fa78 100644
--- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
+++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
@@ -247,6 +247,35 @@ export default function useAutocomplete(props) {

   popupOpen = freeSolo && filteredOptions.length === 0 ? false : popupOpen;

+  if (process.env.NODE_ENV !== 'production') {
+    if (value !== null && !freeSolo && options.length > 0) {
+      const missingValue = (multiple ? value : [value]).filter(
+        value2 => !options.some(option => getOptionSelected(option, value2)),
+      );
+
+      if (missingValue.length > 0) {
+        // eslint-disable-next-line react-hooks/rules-of-hooks
+        console.warn(
+          [
+            `Material-UI: you have provided an out-of-range value${
+              missingValue.length > 1 ? 's' : ''
+            } \`${
+              missingValue.length > 1
+                ? JSON.stringify(missingValue)
+                : JSON.stringify(missingValue[0])
+            }\` for the autocomplete ${id ? `(id="${id}") ` : ''}component.`,
+            '',
+            'Consider providing a value that matches one of the available options.',
+            'You can use the `getOptionSelected` prop to customize the equality test.',
+          ].join('\n'),
+        );
+      }
+    }
+  }
+
   const focusTag = useEventCallback(tagToFocus => {
     if (tagToFocus === -1) {
       inputRef.current.focus();

Do you want to work on a pull request? :)

@support support bot removed the support: Stack Overflow Please ask the community on Stack Overflow label Nov 24, 2019
@oliviertassinari oliviertassinari added docs Improvements or additions to the documentation good first issue Great for first contributions. Enable to learn the contribution process. component: autocomplete This is the name of the generic UI component, not the React module! and removed duplicate This issue or pull request already exists labels Nov 24, 2019
@oliviertassinari oliviertassinari changed the title [AutoComplete] Change default Value from state [Autocomplete] Warn when value and option doesn't match Nov 27, 2019
@thechinedu
Copy link

@oliviertassinari , I'd like to work on this if the op doesn't have the time. Can I go ahead?

@oliviertassinari
Copy link
Member

@blueyedgeek Awesome, feel free to go ahead 👌

@hefedev
Copy link

hefedev commented Feb 6, 2020

@oliviertassinari I'd like to jump on this if no one is currently active.

@oliviertassinari
Copy link
Member

@hefedev Great :) we will also need a test case.

@thechinedu
Copy link

@hefedev, @oliviertassinari I had meant to work on this but some personal issues came up and I couldn't find the time to do it. Glad someone else was able to put in the required effort 👏🏾

@hefedev
Copy link

hefedev commented Feb 10, 2020

I won't be able to work on this anymore. but for the new first timers please check below and make sure you need a test case for it.

https://github.com/mui-org/material-ui/blob/575776f3004c6ac655b128fbdb30bd4b35115ab7/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js#L553

@igorbrasileiro
Copy link
Contributor

@oliviertassinari Can I take this issue?

@oliviertassinari
Copy link
Member

@igorbrasileiro Sure :)

@CHPrado
Copy link

CHPrado commented Mar 31, 2020

I think I'm having a problem now because of this.

I'm using an object as value and options like this:

value: {
    value: agendamento?.origem?.cd_cidade,
    description: agendamento?.origem?.nm_cidade,
 },
options: [{
    value: agendamento?.origem?.cd_cidade,
    description: agendamento?.origem?.nm_cidade,
}],

it is still working like before but now I'm having a spam of warnings.

Any suggestion?

@oliviertassinari
Copy link
Member

@CHPrado Check the getOptionSelected prop.

@CHPrado
Copy link

CHPrado commented Mar 31, 2020

Thanks, this worked:

getOptionSelected: (
    option,
    value,
 ) => value.value === option.value,

@oliviertassinari

This comment has been minimized.

@rmar72
Copy link

rmar72 commented Apr 29, 2020

@oliviertassinari I'm in the same situation as @cdpautsch where we are fetching our possible options from a back-end service. When we filter and remove the previous list of options the previous selected option persists behind the scenes, I say behind scenes because you won't see it as an option from the new list in the dropdown. So here you are looking at the new options and don't select any of them but rather click outside(away from dropdown) that's when the previous selected option just pops up again in the TextField and in the console the warning that suggests getOptionsSelected; which we already have in place.

So just to re-iterate on your guys' convo there's no way around this correct ↓

, are you saying it's important for Material-UI to enforce that currently selected values are always in the available options?

Yes, we can't implement this warning without this constraint. My main fear is that people won't easily understand what's going wrong without it. For instance, in the issue's description.

I understand that this was closed after @igorbrasileiro worked on it, if there's a new way to work with this I totally missed it, was this fixed?

@oliviertassinari highly appreciate your time, thanks!

@oliviertassinari
Copy link
Member

oliviertassinari commented Apr 29, 2020

@rmar72 Apply the same strategy as @cdpautsch to solve the issue, let the component hides the already selected options for you.

@rmar72
Copy link

rmar72 commented Apr 29, 2020

@oliviertassinari appreciate the reply. How is this not an issue with the google maps api example? You are loading new options, getting rid of the previous selected option and these are updating the options given prop and selected value.

@oliviertassinari
Copy link
Member

oliviertassinari commented Apr 29, 2020

@rmar72 Thanks for raising about the warning that http://material-ui.com/components/autocomplete#google-maps-place has. Could you open a new issue?

@finguer
Copy link

finguer commented May 13, 2020

@oliviertassinari , I have a problem when I want to init the value in the AutoComplete
image
I have created the form from json config all it is ok only I have that problem
image
image

image

in that case I am init the value with a json, but the component working good
image
the value and label it is ok when I want to submit only that msg in the console
I have put {...(configInput.initialValue && { value })} because when I use the value={value} in my anothes autocomplete I have other warning when I remove the selected
image

image

and in my component changed for this way
image
the icon for remove the label keep there

@piotros
Copy link
Contributor

piotros commented May 29, 2020

Is there any way to suppress this warning?

@oliviertassinari
Copy link
Member

@piotros By providing the missing option? :)

@piotros
Copy link
Contributor

piotros commented May 29, 2020

@oliviertassinari this is not always possible ;) In my use case options changes on user actions but I want to have all selected values preserved.

Take look at simple simulation of my case: https://stackblitz.com/edit/material-ui-autocomplete

Autocomplete components acts as a multistep select. This works as I expect but this worning... :)

@oliviertassinari
Copy link
Member

oliviertassinari commented May 29, 2020

@piotros Nice "hack" for building a "tree select" component with the Autocomplete 😁. Try this diff:

  return (
    <Autocomplete
      renderInput={params => <TextField {...params} />}
      multiple
      open={open}
      getOptionLabel={({ label }) => label}
-     options={options}
+     options={[...value, ...options]}
+     filterSelectedOptions
      onChange={(e, val) => setValue(val)}
    />
  );

@piotros
Copy link
Contributor

piotros commented Jun 2, 2020

Thank you @oliviertassinari. Works perfect now! :)

@jcable
Copy link

jcable commented Jun 7, 2020

Hi, how do I stop this warning happening when the initial value is nothing selected and on clear?

@flavitsky

This comment has been minimized.

@vince1995
Copy link
Contributor

If anybody is struggling with this around, try combining this:

Thanks, this worked:

getOptionSelected: (
    option,
    value,
 ) => value.value === option.value,

And this:

@piotros Nice "hack" for building a "tree select" component with the Autocomplete 😁. Try this diff:

  return (
    <Autocomplete
      renderInput={params => <TextField {...params} />}
      multiple
      open={open}
      getOptionLabel={({ label }) => label}
-     options={options}
+     options={[...value, ...options]}
+     filterSelectedOptions
      onChange={(e, val) => setValue(val)}
    />
  );

@alainib
Copy link

alainib commented Jul 23, 2020

i have the same warning but i already added getOptionSelected, i make infinity console warning

Material-UI: The value provided to useAutocomplete is invalid.
None of the options match with `{"id":"geonames:6429478","name":"Périgueux"}`.
You can use the `getOptionSelected` prop to customize the equality test.

the code:


  let {
    getRootProps,
    getInputLabelProps,
    getInputProps,
    getListboxProps,
    getOptionProps,
    groupedOptions,
    getOptionSelected,
    focused,
    setAnchorEl,
    anchorEl,
    inputValue
  } = useAutocomplete({
    value: selectedOption || {},
    options: options,
    getOptionLabel: (option) => {
      return option.name || "";
    },
    getOptionSelected: (option, value) =>{
       console.log({value,option});
       // I added id comparaison because in option.name accent char are replaced ( éàè....) 
        return  option?.name.toLowerCase() === value?.name.toLowerCase() || option?.id === value?.id
    },
    selectOnFocus: true
  });

the log from getOptionSelected :

option: {​​
  id: "geonames:6616167",name: "Mareuil en Perigord"
​​}
value: { 
  id: "geonames:6616167", 
  name: "Mareuil en Périgord"
}

Any idea please ?

@OmriAroesti
Copy link

Hi, how do I stop this warning happening when the initial value is nothing selected and on clear?

I personally added the freeSolo flag and then validated the value on onChange(a simple null check)

@nicholasbca
Copy link

nicholasbca commented Aug 25, 2020

I want to init first selection as empty string, but I got warning, any solution please?

const [value, setValue] = React.useState(""); //got warned
const [yearValue, setYearValue] = React.useState(top100Films[0]); not warned
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. 
    in Autocomplete (created by WithStyles(ForwardRef(Autocomplete)))
    in WithStyles(ForwardRef(Autocomplete)) (at demo.js:12)
    in ComboBox (at index.js:6)

here is my code:
https://codesandbox.io/s/autocomplete-set-another-autocomplete-value-uko9b?file=/demo.js

@hinsxd
Copy link
Contributor

hinsxd commented Aug 25, 2020

@jcable @nicholasbca
For single select, I think the empty initial problem can be solved by:

options={
  value === null // depends on what your empty value is
    ? options
    : [value, ...options]
}
filterSelectedOptions

Multiselect has already been solved by @oliviertassinari

@iansjk
Copy link
Contributor

iansjk commented Sep 20, 2020

I had the same problem with warnings for None of the options match with "" when using an Autocomplete with a controlled value.

I ended up using this because I didn't want to enable filterSelectedOptions for UX reasons:

options={[value, ...options]}
filterOptions={(options) =>
  options.filter((option) => option !== "")
}

@Forfold
Copy link

Forfold commented Sep 20, 2020

I have the same issue as @cdpautsch:

We are using useAutocomplete with multi-select and are fetching our possible options from a back-end service. However, some filtering has already been applied to this list, meaning that it's entirely possible that a selected value will not match the currently available options, even when completely valid (just not visible in the current set).

However I was able to get around the strictness of the Autocomplete API with the following code:

export default function MaterialSelect(
  props: CommonSelectProps & (MultiSelectProps | SingleSelectProps),
) {

  . . .

  // https://github.com/mui-org/material-ui/issues/18514
  // be sure to keep the props `filterSelectedOptions` set to hide selected value from dropdown
  // and `getOptionSelected` to ensure what shows as selected for all incoming values
  let options: SelectOption[] = _options
  if (value) {
    // merge options with value
    options = options.concat(Array.isArray(value) ? value : [value])
  }

  return (
    <Autocomplete
      . . .
      filterSelectedOptions
      getOptionSelected={(opt, val) => opt.value === val.value}
  )
}

I originally was using Sets to merge my value and options arrays, and while this worked for selected results that were not in our initial query (we default to showing 5 options when the user focuses the field), if any of the options from the initial dataset are selected the warning still appears. Once I removed using a set allowing duplicates in options, and utilized both filterSelectOptions and getSelectedOption, the issue was resolved.

@ffjeanette
Copy link

This is my case. I have a component wrapping some form elements.
The data used for the autocomplete value is passed in as a prop to this wrapper component. The draft.valueX can be undefined.
As iansjk I didnt want to use the filterSelectedOptions prop for ux reasons, but I need the users to be able to filter the options.
I dont like it, but this is my solutions for now.

type Option = {
   value: string
   label: string
}
type Draft = {
   valueX?: string
   . . . 
}

const defaultFilterOptions = createFilterOptions<Option>()

const MyComponent = ({ draft, ...}: { draft: Draft.... }) => {

  const option: Option = useMemo(() => ({
    value: draft.valueX ? draft.valueX  : '',
    label: draft.valueX ? getOptionLabel(draft.valueX ) : ''
  }), [draft])
  
  return (
      <Autocomplete
      options={!option.value ? [option, ...options] : options}
      filterOptions={(options: Option[], state) => {
          //use default filter and then remove "empty" initial value if needed
          const defaultFiltered = defaultFilterOptions(options, state)
          return defaultFiltered.filter((option) => option.value !== '')
      }}
    . . . 
  />

@ifeltsweet
Copy link

We had a similar issue where we were getting filtered options from the backend depending on the current input value. As such, the currently selected value may or may not have been present in the options returned by the backend which in turn triggered the warning everyone is talking about here.

To solve this we did the following:

<Autocomplete
  options={[value, ...serverOptions]} // Currently selected value must be preset to not trigger the missing value warning.
  filterOptions={() => serverOptions} // But we only show what BE returned to us, so there are no duplicates.
  value={value}
  onChange={(_, value) => onChange(value)}
  onInputChange={(_, value) => setSearchQuery(value)} // Triggers BE to return filtered options.
/>

This works exactly how we want it to work, including that the selected option is highlighted.

@giladv
Copy link

giladv commented Jul 4, 2022

Just wanted to add these warnings do more harm than good.
Requires so much hacks to make it happy so i won't warn when options are dynamic (from server).

@kcarmonamurphy
Copy link

I followed the suggestion from @ifeltsweet and I was able to make this warning disappear. For context, I had three <Autocomplete> fields whose values were filtered from the selections of the previous ones.

@NoelBq
Copy link

NoelBq commented Feb 7, 2023

  options={[value, ...serverOptions]} // Currently selected value must be preset to not trigger the missing value warning.
  filterOptions={() => serverOptions} // But we only show what BE returned to us, so there are no duplicates.
  value={value}

this worked for me. I had a reset button and it was virtually impossible to reset the autocomplete without getting that hideous warning. Thanks so much ✨

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: autocomplete This is the name of the generic UI component, not the React module! docs Improvements or additions to the documentation good first issue Great for first contributions. Enable to learn the contribution process.
Projects
None yet
Development

Successfully merging a pull request may close this issue.