A component is changing an uncontrolled Autocomplete to be controlled
Asked Answered
P

7

58

Can you tell me that why I'm getting error "A component is changing an uncontrolled Autocomplete to be controlled. Elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled Autocomplete element for the lifetime of the component."

component :


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}
    />
  );
}

calling autocomplte:

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

how can I solve this error?

Pleiades answered 7/8, 2020 at 5:48 Comment(4)
Interesting. Will you be able to provide a codesandbox for this?Painterly
I think it's related mix controlled input with uncontrolled: react-hook-form.com/faqs#WhyisfirstkeystrokeisnotworkingWilkes
what helped me is setting defaultValue={null} on <Controller ...Genu
Did you manage to solve this error? If so how ?Gorse
F
50

You ensured that the value property never had been undefined, but you had to do same for inputValue.

  1. the "value" state with the value/onChange props combination. This state represents the value selected by the user, for instance when pressing Enter.
  2. the "input value" state with the inputValue/onInputChange props combination. This state represents the value displayed in the textbox.

⚠️ These two state are isolated, they should be controlled independently.

Component becomes uncontrolled when inputValue property is undefined, and vice versa.

If in the following example you delete an empty string from React.useState('') you'll get the same error message because inputValue during first render is 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>
  )
}

Firmament answered 7/8, 2020 at 10:54 Comment(2)
I use Autocomplete component with value props, which is controlled by state. By removing value={this.state.imageId} prop form the Autocomplete it does not show any error now.Ursi
@Ursi you saved meThinnish
A
43

When no value is selected, you need to add || null to prevent the Autocomplete going into uncontrolled mode:

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

If you pass value={undefined} to the Autocomplete component, it will start in "uncontrolled" mode, meaning it keeps its own internal state. Then if you later supply a value it raises the "A component is changing" error. But if you pass value={null}instead of value={undefined} that causes the Autocomplete to start in controlled mode. The Autocomplete will assume you will be providing the state, not keep its own, and the error goes away.

Ambulant answered 6/5, 2022 at 5:12 Comment(2)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Marchelle
This was my issue; you have to confirm that the value attribute cannot be undefined. By adding || null you provide a default value and remove the console errorJorum
M
6

I solved this by removing the default value.

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

It wasn't obvious how to solve this, and the documentation doesn't help much either. I find it curious that a copy-pasted example from the documentation results in this error. I guess the example works because the choices are hard-coded.

Marnamarne answered 17/8, 2021 at 7:25 Comment(1)
It is worth mentioning here that in lieu of defaultValue= you can use value={} and avoid the Warning in the console ..Iridium
V
3

I had the same issue today, but I was able to solve it by providing a default value of null as well as providing a null value in case of it not existing. I'll leave the code that worked for me:

<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: '' });
           }
    }}
/>
Vilmavim answered 17/1, 2023 at 3:12 Comment(0)
L
2

Previous answer was absolutely correct, BUT I spend 20 minutes while I figure out that inputValue it should be value So working example from me:

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} />}
/>
Luminescence answered 12/5, 2022 at 5:3 Comment(0)
P
2

For me I fixed this issue by updated the onChange function and add || null rather then removing the default value since I still need it here is the code

<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>

For the autocomplete with options which is a simple array without object you can simply do it that way and u want get any issue

<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>

the default value you can set it within useForm if you are using react-hook-form

const {
  handleSubmit,
  control,
  watch,
  register,
  formState: { errors },
} = useForm({
  defaultValues: {
    ...
    type: questionData?.type ? mapperQuestionType[questionData?.type] : '',
  },
})
Piquet answered 14/2, 2023 at 19:51 Comment(0)
A
0

For me, the issue was that my component returned an <Autocomplete> component with a different set of props if it was in a loading state, compared to the loaded state. The former set of props indicated an uncontrolled <Autocomplete> whereas the latter indicated a controlled one:

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" />}
    />
  );
};
Anglicize answered 1/12, 2023 at 10:55 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.