一个组件正在将一个不受控制的自动完成组件转换为受控制的。

53

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

组件:


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  || ''}
                  />
                } />

我该如何解决这个错误?


有趣。你能提供一个 Codesandbox 吗? - bertdida
我认为这与混合控制输入和不受控制有关:https://react-hook-form.com/faqs#Whyisfirstkeystrokeisnotworking - Bill
2
帮助我的是在<Controller ...上设置defaultValue={null} - WelcomeTo
你解决了这个错误吗?如果是,怎么解决的? - andres
7个回答

46

您确保value属性从未为undefined,但同样需要对inputValue进行相同处理。

  1. 使用value/onChange组合的"value"状态。此状态表示用户选择的值,例如按Enter键时。
  2. 使用inputValue/onInputChange组合的"输入值"状态。此状态表示文本框中显示的值。

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

如果inputValue属性为undefined,则组件变为不受控制,反之亦然。

如果在以下示例中从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>
  )
}


1
我使用带有value属性的Autocomplete组件,该属性由状态控制。通过从Autocomplete中删除value={this.state.imageId}属性,现在不会显示任何错误。 - akshay_sushir
@akshay_sushir 你救了我。 - Or Assayag

36

当没有选定任何值时,您需要添加|| null以防止自动完成进入不受控制的模式:

<Autocomplete {...props} value={props.value || null} />
如果你将 value={undefined} 传递给 Autocomplete 组件,则它将以“不受控制”的模式启动,这意味着它保留自己的内部状态。然后,如果您稍后提供了一个value,它会引发“组件正在更改”的错误。但是,如果您传递value={null}而不是value={undefined},则会导致 Autocomplete 以受控模式启动。Autocomplete 将假定您将提供状态,而不是保留其自己的状态,并且该错误将消失。

目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何撰写好答案的更多信息。 - Community
这是我的问题;你必须确认value属性不能为undefined。通过添加|| null,您提供了一个默认值并删除了控制台错误。 - G M

6

我通过删除默认值来解决了这个问题。

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

如何解决这个问题并不明显,文档也没有提供太多帮助。我发现从文档中复制粘贴的例子竟然导致了这个错误,这让我感到很奇怪。我猜测那个例子之所以能工作是因为选项是预设的。


值得在这里提到的是,你可以使用value={}代替defaultValue=,从而避免控制台中出现警告。 - Zak

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分钟才想明白输入值应该是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
对我来说,问题在于我的组件在加载状态和加载完成状态下返回了一个不同的属性集的组件。前者的属性集表示一个不受控制的,而后者表示一个受控制的。
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" />}
    />
  );
};

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