组件正在将不受控制的自动完成更改为受控制

问题描述 投票:0回答:7

你能告诉我为什么我收到错误“组件正在将不受控制的自动完成更改为受控制。 元素不应从不受控制切换到受控制(反之亦然)。 决定在组件的生命周期内使用受控或不受控的自动完成元素。”

成分:


function AutoComplete(props) {

  const defaultProps = {
    options: props.options,
    getOptionLabel: option => option.name,
  };

  const handleChange = (e, value) => {
    props.onChange(value);
  };

  return (
    <Autocomplete
      {...defaultProps}
      renderInput={params => (
        <TextField {...params} label={props.label} margin="normal" />
      )}
      onChange={handleChange}
      value={props.value}
    />
  );
}

调用自动完成:

               <Controller
                control={control}
                name = 'country'
                as = {
                  <AutoComplete
                    options={countryOptions}
                    onChange={selectCountryHandler}
                    label="Country"
                    value={selectedCountry  || ''}
                  />
                } />

如何解决这个错误?

reactjs autocomplete controller material-ui react-hook-form
7个回答
46
投票

您确保 value 属性从未未定义,但您必须对 inputValue 执行相同操作。

  1. 带有 value/onChange 属性组合的“value”状态。此状态表示用户选择的值,例如按 Enter 键时。
  2. “输入值”状态与 inputValue/onInputChange 属性组合。该状态代表文本框中显示的值。

⚠️这两个状态是隔离的,应该独立控制。

inputValue 属性未定义时,组件将变得不受控制,反之亦然。

如果在下面的示例中您删除了一个空字符串

React.useState('')
您将收到相同的错误消息,因为第一次渲染期间的inputValue
undefined

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

const options = ['Option 1', 'Option 2']

export default function AutocompleteLab() {
  const [value, setValue] = React.useState(options[0])
  const [inputValue, setInputValue] = React.useState('')

  return (
    <div>
      <div>{`value: ${value !== null ? `'${value}'` : 'null'}`}</div>
      <div>{`inputValue: '${inputValue}'`}</div>
      <br />
      <Autocomplete
        value={value}
        onChange={(_, newValue) => {
          setValue(newValue)
        }}
        inputValue={inputValue}
        onInputChange={(_, newInputValue) => {
          setInputValue(newInputValue)
        }}
        options={options}
        style={{ width: 300 }}
        renderInput={(params) => <TextField {...params} label="Name" variant="outlined" />}
      />
    </div>
  )
}


36
投票

未选择任何值时,需要添加

|| null
以防止自动完成进入不受控制模式:

<Autocomplete {...props} value={props.value || null} />

如果将

value={undefined}
传递给自动完成组件,它将以“不受控制”模式启动,这意味着它保持自己的内部状态。然后,如果您稍后提供
value
,则会引发“A 组件正在更改”错误。但是,如果您传递
value={null}
而不是
value={undefined}
,则会导致自动完成以受控模式启动。自动完成将假设您将提供状态,而不是保留其自己的状态,并且错误会消失。


6
投票

我通过删除

default value
解决了这个问题。

             <Autocomplete
                multiple
                id="multiple-limit-tags"
                options={(option) => option.label}
                getOptionLabel={(option) => option}
                // defaultValue={options || []}
                renderInput={(params) => <TextField {...params} label="My Label" />}           
              />

如何解决这个问题并不明显,而且文档也没有多大帮助。我觉得奇怪的是,文档中的复制粘贴示例会导致此错误。我想这个例子是有效的,因为选择是硬编码的。


3
投票

我今天遇到了同样的问题,但我能够通过提供默认值 null 以及在它不存在的情况下提供 null 值来解决它。我将留下对我有用的代码:

<Autocomplete
    value={filters.tag || null}
    defaultValue={null}
    options={tags || []}
    getOptionLabel={(option) => option}
    renderInput={(params) => (
        <TextField {...params} label="Search" variant='outlined' size='small' />
    )}
    fullWidth
    onChange={(event, value) => {
           if (value) {
               setFilters({ ...filters, tag: value });
           } else {
               setFilters({ ...filters, tag: '' });
           }
    }}
/>

2
投票

之前的答案绝对正确,但是我花了 20 分钟才弄清楚 inputValue 应该是 value 我的工作示例:

export default function AddModal(): ReactElement {
const [resource, setResource] = useState('one');
<Autocomplete
    id="autocomplete"
    options={['one', 'two']}
    defaultValue={resource}
    value={resource}
    PopperComponent={StyledPopper}
    onChange={(event, newInputValue) => setResource(newInputValue)}
    renderInput={(params) => <TextField {...params} />}
/>

1
投票

对我来说,我通过更新

onChange
函数并添加
|| null
解决了这个问题,而不是删除默认值,因为我仍然需要它,这里是代码

<Box mt={2}>
  <Controller
    control={control}
    name="thematic"
    rules={{
      required: 'Veuillez choisir une réponse',
    }}
    render={({ field: { onChange } }) => (
      <Autocomplete
        defaultValue={
          questionData?.thematic ? questionData?.thematic : null
        }
        options={thematics}
        getOptionLabel={(option) => option.name}
        onChange={(event, values) => {
          onChange(values || null)
        }}
        renderInput={(params) => (
          <TextField
            {...params}
            label="Thématique"
            placeholder="Thématique"
            helperText={errors.thematic?.message}
            error={!!errors.thematic}
          />
        )}
      />
    )}
  />
</Box>

对于带有选项的自动完成功能,这是一个没有对象的简单数组,您可以简单地这样做,并且您想得到任何问题

<Box mt={2}>
  <Controller
    control={control}
    name="type"
    rules={{
      required: 'Veuillez choisir une réponse',
    }}
    render={({ field: { onChange, value } }) => (
      <Autocomplete
        freeSolo
        options={['Champ', 'Sélection', 'Choix multiple']}
        onChange={(event, values) => onChange(values)}
        value={value}
        renderInput={(params) => (
          <TextField
            {...params}
            label="Type de la question"
            variant="outlined"
            onChange={onChange}
            helperText={errors.type?.message}
            error={!!errors.type}
          />
        )}
      />
    )}
  />
</Box>

如果您使用react-hook-form,您可以在

useForm
内设置默认值

const {
  handleSubmit,
  control,
  watch,
  register,
  formState: { errors },
} = useForm({
  defaultValues: {
    ...
    type: questionData?.type ? mapperQuestionType[questionData?.type] : '',
  },
})

0
投票

对我来说,问题是我的组件返回一个

<Autocomplete>
组件,如果它处于加载状态,与加载状态相比,它具有不同的 props 集。前一组道具表示不受控制的
<Autocomplete>
,而后者表示受控制的:

import {Autocomplete, TextField} from '@mui/material';
import React, {useState} from 'react';

export type MyComponentProps =
  | {
      loading: true;
    }
  | {
      loading: false;
      options: string[];
      value: string;
      setValue: (value: string) => void;
    };

export const MyComponent = (props: MyComponentProps) => {
  const [inputValue, setInputValue] = useState('');

  if (props.loading) {
    return (
      <Autocomplete
        loading
        disabled
        id="my-component"
        options={[]}
        renderInput={(params) => <TextField {...params} label="My Component" />}
        // Adding these two props fixed the error message:
        // value={null}
        // inputValue=""
      />
    );
  }

  return (
    <Autocomplete
      value={props.value}
      onChange={(_, newValue) => {
        props.setValue(newValue ?? '');
      }}
      id="my-component"
      options={props.options}
      inputValue={inputValue}
      onInputChange={(_, newInputValue) => {
        setInputValue(newInputValue);
      }}
      renderInput={(params) => <TextField {...params} label="My Component" />}
    />
  );
};
© www.soinside.com 2019 - 2024. All rights reserved.