Material-ui 自动完成组件警告:提供给自动完成组件的值无效。

86

我正在使用React和material-ui。我刚刚意识到在尝试提交表单时,Autocomplete组件出现了警告,所以我尝试做一些非常基本的东西,就像文档中的示例:

let Form = props => {

  return(
        <form noValidate onSubmit={handleSubmit} >
            <Autocomplete
                id="combo-box-demo"
                options={[{id:1,name:"test"},{id:2, name:"test2"}]}
                getOptionLabel={(option) => option.name}
                style={{ width: 300 }}
                renderInput={(params) => <TextField {...params} label="Combo box" variant="outlined" />}
            />

当我尝试提交表单时,出现以下错误:

Material-UI:提供给 Autocomplete 的值无效。 没有一个选项与 {"id":1,"name":"test"} 匹配。 您可以使用 getOptionSelected 属性自定义相等性测试。

我也发现,如果将选项设置在组件状态中,则不会出现警告(只有在它们像常量一样设置时才会出现)。所以我想知道你们中是否有人了解这种行为?非常感谢。

10个回答

170

基本上,您收到警告的原因是在4.x.x版本中getOptionSelected的默认实现:

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

在您的情况下,选择一个值后会进行以下比较:

// option === value:
{id:1, name:"test"} === {id:1, name:"test"} // false

显然,在某些情况下它可以是true。在这种特定情况下,由于对象指向不同的实例,它是false

解决方案!需要覆盖getOptionSelected的实现:

<Autocomplete
 getOptionSelected={(option, value) => option.id === value.id}
 ...otherProps
/>

[更新]注意,在版本5.x.x中,该属性已更名:

-  getOptionSelected={(option, value) => option.id === value.id}
+  isOptionEqualToValue={(option, value) => option.id === value.id}

19
在MUI的第5个版本中,getOptionSelected已更名为isOptionEqualToValue。 - MintWelsh
7
谢谢。但是如果我的自动完成功能从后端获取选项,而且没有任何新选项与旧值匹配怎么办? - eagor
如果从服务器中传来的内容是异步的,你可以检查选项是否仍在加载中,以返回true。因为否则,它将尝试在空列表中搜索选择。 - Calin Vlasin
isOptionEqualToValue={(option, value) => option.id === value.id} 这个解决了我在将 Formik 和 Material UI 结合使用时遇到的问题 - 将其放置在 {...field} 参数下。 - The Old County
1
但是如果我不想选择任何选项呢?没有选项与选定状态匹配也完全没问题。我仍然希望控制组件,只是不选中任何选项。这似乎不可能,是吗? - undefined
显示剩余2条评论

28

版本 5.0

isOptionEqualToValue={(option, value) => option.value === value.value}

24

当您想构建一个搜索器并且输入的值不一定与选项相同时,您可以将freeSolo设置为true,这样警告将消失。


9
这个答案应该更加专注。我正在构建一个异步自动完成功能(在输入内容改变时向API发送请求),这是解决警告的唯一方法。 - Cafn
11
为了跟进这个问题,设置 freeSolo 将会移除自动完成组件右侧的弹出按钮(下拉箭头)。如果要保留该按钮,您还应该添加 forcePopupIcon={true} - Nathan F.

8

7
以下是关于 elVengadors Answer 的跟进内容:
如果您想构建一个搜索器,其中您在框中输入的值(inputValue)不一定是提供的选项之一,则可以将 freeSolo 设置为 true。 这将停止显示警告消息。 如果您正在创建一个允许异步查询API的组件,则可能需要这样做。这将导致 options 的值基于API的响应而发生变化,但在将 inputValue 更改为查询API之前选择的选项可能不包括在这个新选项列表中。
Autocomplete 组件文档 中,描述了 freeSolo

如果为 true,则 Autocomplete 是自由独奏的,这意味着用户输入不限于提供的选项。

奖励:
freeSolo 设置为 true 将删除弹出按钮(Autocomplete 组件右侧的下拉箭头)。 如果要保留此按钮,则还应添加 forcePopupIcon={true}

如果我想要移除警告,但又不想启用自由选择功能怎么办?也就是说,只允许用户从可变选项数组中选择数值。 - undefined

0
MUI Autocomplete 可搜索组件 + RFA (react From Hook)。
将默认值设置为 null,这样就可以消除警告。

 <RHFAutocomplete
            name="responsible"
            options={responsibles.map((r) => ({ _id: r._id, label: r.name }))}
            label="Responsable magasin"
            onValueChanges={(value) => setResponsibleFilter(value)}
          />

在我的自动完成组件中,我添加了一个函数属性来获取输入值,称为onValueChanges

 <Controller
      name={name}
      control={control}
      render={({ field, fieldState: { error } }) => (
        <Autocomplete
          {...field}
          onChange={(event, newValue) => setValue(name, newValue, { shouldValidate: true })}
          renderInput={(params) => (
            <TextField
              label={`${translate(label)}`}
              error={!!error}
              onChange={(e) => onValueChanges ? onValueChanges(e.target.value) : {}}
              helperText={error ? error?.message : helperText}
              {...params}
            />
          )}
          {...other}
        />
      )}
    />

Here is the final output. mui autocomplete searchable component + RFA


0

在添加getOptionSelected后,我遇到了同样的问题,错误消失了。

错误:

<Autocomplete
   fullWidth={true}
   label={'Location'}
   margin={'noraml'}
   multiple={false}
   name={'location'}
   value={formValues.location === '' ? {label: ''} : {label: formValues.location}}
   options={location}
   ref={locationRef}
   onChange={useCallback((e, v) => handleInputChange(e, v))}
/>

解决方案:添加getOptionSelected属性

<Autocomplete
   fullWidth={true}
   label={'Location'}
   margin={'noraml'}
   multiple={false}
   name={'location'}
   getOptionSelected={useCallback((option, value) => option.value === value.value)} // added
   value={formValues.location === '' ? {label: ''} : {label: formValues.location}}
   options={location}
   ref={locationRef}
   onChange={useCallback((e, v) => handleInputChange(e, v))}
/> 

0
const CustomAutocomplete = ({
  value,
  onChange,
  options,
  name,
  label,
  error,
  inputRef,
  onBlur,
}) => {
  const acValue = useMemo(() => {
    return options.find((option) => option.value === value);
  }, [options, value]);
  return (
    <Autocomplete
      value={acValue}
      disablePortal
      onChange={(e, v) => {
        onChange(v?.value);
      }}
      options={options}
      getOptionLabel={(o) => {
        return o.title;
      }}
      renderInput={(params) => (
        <UnControlledTextField
          {...params}
          fullWidth
          onBlur={onBlur}
          inputRef={inputRef}
          name={name}
          label={label}
          error={error}
        />
      )}
    />
  );
};

在我的情况下,我正在使用自定义选项并仅将值存储在表单数据中。
我将选项保留为:
{ title, value }

onChange 只存储选项的 value 部分


-1

对于那些也遇到错误/警告的人,useAutocomplete.js:210 MUI:提供给自动完成组件的值无效。没有任何选项与""匹配。您可以使用isOptionEqualToValue属性来自定义相等性测试。

并且对于defaultValue属性也存在困扰。

确保在初始化钩子时,不要将空字符串作为默认值传递给您的自动完成变量值。

  const expenseForm = useForm<ExpenseFormInput>({
    defaultValues: {
      autocompleteval: '',
    },
  });

它将覆盖并替换您传递给组件的defaultValue值,并触发令人讨厌的 MUI 警告。


-7

我认为你不应该使用<form>来包装AutoComplete组件。你应该为AutoComplete设置值,并使用一个函数来处理点击按钮提交的事件。
尝试这样做:

let Form = props => {
  const [value, setValue] = useState({})
  
  const handleOnSubmit = (value) => {
    setValue(value)
    ...
  }

  return(
        <div>
            <Autocomplete
                id="combo-box-demo"
                value={value}
                options={[{id:1,name:"test"},{id:2, name:"test2"}]}
                getOptionLabel={(option) => option.name}
                style={{ width: 300 }}
                renderInput={(params) => <TextField {...params} label="Combo box" variant="outlined" />}
            />
            <Button onClick={() => handleOnSubmit(value)}>Submit</Button>
        </div>
  )
}
            


1
谢谢你的回答,Michael。你介意告诉我原因吗?因为自动完成属于一个有许多其他必需字段的表单。 - Julio Lopez
@JulioLopez 根据我的经验,我通常使用这种模式来处理表单。我感觉我对组件有更多的控制权。我可以控制哪些数据字段将被提交,而不依赖于表单。如果您想在提交之前在组件的其他部分使用其中一个数据字段怎么办?如果您有许多其他字段,您应该像我在AutoComplete组件中所做的那样为这些字段设置值。如果您的表单中有三个以上的字段,则可以使用useReducer来存储和操作数据字段。 - Michael
@wwwebman的回答帮了我。 - DragoRaptor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接