A component is changing an uncontrolled input of type text to be controlled error in ReactJS
Asked Answered
L

28

861

Warning: A component is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.*

Following is my code:

constructor(props) {
  super(props);
  this.state = {
    fields: {},
    errors: {}
  }
  this.onSubmit = this.onSubmit.bind(this);
}

....

onChange(field, e){
  let fields = this.state.fields;
  fields[field] = e.target.value;
  this.setState({fields});
}

....

render() {
  return(
    <div className="form-group">
      <input
        value={this.state.fields["name"]}
        onChange={this.onChange.bind(this, "name")}
        className="form-control"
        type="text"
        refs="name"
        placeholder="Name *"
      />
      <span style={{color: "red"}}>{this.state.errors["name"]}</span>
    </div>
  )
}
Luigiluigino answered 30/10, 2017 at 9:47 Comment(4)
what is the initial value of fields in state?Wollis
constructor(props) { super(props); this.state = { fields: {}, errors: {} } this.onSubmit = this.onSubmit.bind(this); }Luigiluigino
Possible duplicate of React - changing an uncontrolled inputKarnak
I just discovered you can use useRef to conditionally set the value to the current input, e.g. value={amountInputFocused ? amountRef.current?.value : amountState}. Not sure if this is by design, but it works, and silences the error.Ebenezer
W
1466

The reason is, in state you defined:

this.state = { fields: {} }

fields as a blank object, so during the first rendering this.state.fields.name will be undefined, and the input field will get its value as:

value={undefined}

Because of that, the input field will become uncontrolled.

Once you enter any value in input, fields in state gets changed to:

this.state = { fields: {name: 'xyz'} }

And at that time the input field gets converted into a controlled component; that's why you are getting the error:

A component is changing an uncontrolled input of type text to be controlled.

Possible Solutions:

1- Define the fields in state as:

this.state = { fields: {name: ''} }

2- Or define the value property by using Short-circuit evaluation like this:

value={this.state.fields.name || ''}   // (undefined || '') = ''
Wollis answered 30/10, 2017 at 9:54 Comment(24)
is there a reason undefined is "uncontrolled" while the latter is "controlled" - what do these mean?Bankbook
@PrashanthSubramanian Maybe this could help understand it better. reactjs.org/docs/forms.html#controlled-componentsSporades
I don't have any undefined in my state during the change eventAixenprovence
@AlexeySh. can you explain more? didn't get you.Wollis
you did write that input field will get value as:value={undefined}. right? I don't have such value. my state already has a value: this.state = { settings: {accountNumber: ''} }Aixenprovence
it will get value as undefined in this case: this.state = { settings: {} } and accessing this.state.settings.accountNumber . are you getting that error?Wollis
@MayankShukla Why React doesn't complain about this: value={this.state.fields.name || ''} ? Isn't that value changing also from an uncontrolled '' to a controlled this.state.fields.name ? I know it works! But I have always wondered why it doesn't trigger the same error? Do you know?Cruelty
because '' is a valid string value. so when you change '' to 'xyz', input type is not changing means controlled to controlled. But in case of undefined to xyz type will change from uncontrolled to controlled.Wollis
Thank you this worked for me: value={this.state.fields.name || ''}Thermaesthesia
In my case this doesn't work. Not sure why. But changing value to defaultValue helped solve the problem.Foothill
@Foothill in that case field will be uncontrolled, not controlled.Wollis
I didn't know that once the value becomes undefined the input became automatically uncontrolled, Great. Already fix my problemBertolde
short-circuit did the trick. I was setting value field from state as well and even tried setting the field inside state to an empty string but nothing happened.Gimmal
amazing! because i did't see this mentioned in the official docs, or am i blind? link to source please :)Turbidimeter
@Bondolin, but it is also not allowing user to edit textfieldHeterochromous
note that this is also true vice versa like if you are setting a state property's default value to undefined.Eolande
First way is good solution. It is working well. But what to do when you don't know the property values of fields?Nicolenicolea
I don't know what is wrong with this behaviour. why it's a warningDexamethasone
@PrashanthSubramanian A component with 'undefined' state value is considered uncontrolled component because, a controlled component should have its data-type predefined so as to ensure that it can accept the value stored in the state i.e. a radio button cannot accept text input and so on...Hollah
Sometimes, I feel stackoverflow should be declared the official docs for so many great libraries because of people like you who are so great at explanations for a beginners like us. Thank you great sirSamy
I finally understand the controlled vs uncontrolled based on your answer. value || '' solved my issue!Leola
mayank sukla , bodolin ... you both are ultra genius. Thank You so much...Curzon
FANTASTIC answer! I think FB should consider updating the error, it's absolutely not clear an empty string is considered a value while undefined is not, as JS is strongly dynamic and often an empty string and undefined are treated very similarly..Myramyrah
I have a question. If my field is of type string I can use empty string as default value, if my field is boolean I can use false but if my field is a number (e.g. an input field where the user has to specify his age) I can't put 0 as default value, will be meaningless on user side. How I can avoid that warning ? Because now I have to put undefined and then when the user interact I can change with the value he want (I'm using controlled component to manage my form).Alithea
I
124

Changing value to defaultValue will resolve it.

Note:

defaultValue is only for the initial load. If you want to initialize the input then you should use defaultValue, but if you want to use state to change the value then you need to use value. Read this for more.

I used value={this.state.input ||""} in input to get rid of that warning.

Intemperate answered 7/10, 2019 at 12:47 Comment(3)
Thank you! I came across this problem and the other solutions didn't apply to me, but this solution helped.Secretive
you can apply the same answer for functional component also. Example: value={user || ""}Certify
This one worked for me. I was using my companies special DLS input components and when I changed value to defaultValue for the specific input field, the field stopped resetting and losing state and the problem went away!Lev
K
57

Inside the component put the input box in the following way.

<input
    className="class-name"
    type= "text"
    id="id-123"
    value={ this.state.value || "" }
    name="field-name"
    placeholder="Enter Name"
/>
Kunlun answered 12/11, 2019 at 9:39 Comment(0)
P
41

In addition to the accepted answer, if you're using an input of type checkbox or radio, I've found I need to null/undefined check the checked attribute as well.

<input
  id={myId}
  name={myName}
  type="checkbox" // or "radio"
  value={myStateValue || ''}
  checked={someBoolean ? someBoolean : false}
  />

And if you're using TS (or Babel), you could use nullish coalescing instead of the logical OR operator:

value={myStateValue ?? ''}
checked={someBoolean ?? false}
Prater answered 18/1, 2020 at 8:53 Comment(1)
it's simple thinking that solves complex things, cool @Lynden NoyeMaroney
P
34

SIMPLY, You must set initial state first

If you don't set initial state react will treat that as an uncontrolled component

Photoemission answered 15/9, 2019 at 17:33 Comment(0)
S
25

that's happen because the value can not be undefined or null to resolve you can do it like this

value={ this.state.value ?? "" }
Sialagogue answered 5/3, 2022 at 0:29 Comment(0)
F
19
const [name, setName] = useState()

generates error as soon as you type in the text field

const [name, setName] = useState('') // <-- by putting in quotes 

will fix the issue on this string example.

Frangipani answered 13/12, 2019 at 20:41 Comment(0)
T
15

As mentioned above you need to set the initial state, in my case I forgot to add ' ' quotes inside setSate();

  const AddUser = (props) => {
  const [enteredUsername, setEnteredUsername] = useState()
  const [enteredAge, setEnteredAge] = useState()

Gives the following error

enter image description here

Correct code is to simply set the initial state to an empty string ' '

  const AddUser = (props) => {
  const [enteredUsername, setEnteredUsername] = useState('')
  const [enteredAge, setEnteredAge] = useState('')
Trumpeter answered 6/6, 2021 at 9:17 Comment(2)
Exactly that was the mistake!! thanks a lot.Larder
I was having the same problem only that instead of using an empty string as initial value I was using undefined. Switching to an empty string fixed the problem. ThanksNetherlands
W
13

Set Current State first ...this.state

Its because when you are going to assign a new state it may be undefined. so it will be fixed by setting state extracting current state also

this.setState({...this.state, field})

If there is an object in your state, you should set state as follows, suppose you have to set username inside the user object.

this.setState({user:{...this.state.user, ['username']: username}})
Waldheim answered 5/5, 2019 at 22:40 Comment(1)
As far as I understand it, setState already only overrides fields, so in the first case it should be unnecessary to destructure assign. In the second state it may be necessary since setState will replace a sub-object wholesale.Iodate
A
10

Best way to fix this is to set the initial state to ''.

constructor(props) {
 super(props)
  this.state = {
    fields: {
      first_name: ''
    }
  }
  this.onChange = this.onChange.bind(this);
}

onChange(e) {
  this.setState({
    fields:{
     ...this.state.fields,
     [e.target.name]: e.target.value
    }
  })
}


render() {
  return(
    <div className="form-group">
      <input
        value={this.state.fields.first_name}
        onChange={this.onChange}
        className="form-control"
        name="first_name" // Same as state key
        type="text"
        refs="name"
        placeholder="Name *"
      />
      <span style={{color: "red"}}>{this.state.errors.first_name}</span>
    </div>
  )
} 

Then you can still run your checks like if (field) and still achieve the same result if you have the value as ''.

Now since your value is now classified as type string instead of undefined after evaluation. Thus, clearing the error from the console of a big red block 😁😎.

Allisan answered 4/11, 2021 at 4:27 Comment(0)
P
7

I am new to reactjs and I am using version 17 of reactjs I was getting this problem

I solved:

Instead of this

const [email, setEmail] = useState();

I added this

const [email, setEmail] = useState("");

In useState function I added quotes to initialize the data and the error was gone.

Postexilian answered 21/4, 2022 at 20:19 Comment(0)
A
5

Put empty value if the value does not exist or null.

value={ this.state.value || "" }
Assertion answered 5/8, 2020 at 5:9 Comment(1)
Thanks Max! This is the cause of the problem, however, your code snippet will not work for this user as is (this.state.fields.name vs this.state.value)Phox
R
5

If you're setting the value attribute to an object's property and want to be sure the property is not undefined, then you can combine the nullish coalescing operator ?? with an optional chaining operator ?. as follows:

<input
  value={myObject?.property ?? ''}
/>
Rhines answered 21/1, 2022 at 7:38 Comment(0)
M
4

In my case it was pretty much what Mayank Shukla's top answer says. The only detail was that my state was lacking completely the property I was defining.

For example, if you have this state:

state = {
    "a" : "A",
    "b" : "B",
}

If you're expanding your code, you might want to add a new prop so, someplace else in your code you might create a new property c whose value is not only undefined on the component's state but the property itself is undefined.

To solve this just make sure to add c into your state and give it a proper initial value.

e.g.,

state = {
    "a" : "A",
    "b" : "B",
    "c" : "C", // added and initialized property!
}

Hope I was able to explain my edge case.

Month answered 31/3, 2020 at 6:57 Comment(0)
R
4

If you use multiple input in on field, follow: For example:

class AddUser extends React.Component {
   constructor(props){
     super(props);

     this.state = {
       fields: { UserName: '', Password: '' }
     };
   }

   onChangeField = event => {
    let name = event.target.name;
    let value = event.target.value;
    this.setState(prevState => {
        prevState.fields[name] =  value;
        return {
           fields: prevState.fields
        };
    });
  };

  render() { 
     const { UserName, Password } = this.state.fields;
     return (
         <form>
             <div>
                 <label htmlFor="UserName">UserName</label>
                 <input type="text" 
                        id='UserName' 
                        name='UserName'
                        value={UserName}
                        onChange={this.onChangeField}/>
              </div>
              <div>
                  <label htmlFor="Password">Password</label>
                  <input type="password" 
                         id='Password' 
                         name='Password'
                         value={Password}
                         onChange={this.onChangeField}/>
              </div>
         </form>
     ); 
  }
}

Search your problem at:

onChangeField = event => {
    let name = event.target.name;
    let value = event.target.value;
    this.setState(prevState => {
        prevState.fields[name] =  value;
        return {
            fields: prevState.fields
        };
    });
};
Rollerskate answered 17/4, 2020 at 9:2 Comment(0)
M
4

Using React Hooks also don't forget to set the initial value.
I was using <input type='datetime-local' value={eventStart} /> and initial eventStart was like

const [eventStart, setEventStart] = useState();
instead
const [eventStart, setEventStart] = useState('');.

The empty string in parentheses is difference.
Also, if you reset form after submit like i do, again you need to set it to empty string, not just to empty parentheses.

This is just my small contribution to this topic, maybe it will help someone.

Maltzman answered 24/5, 2020 at 8:48 Comment(0)
H
4

like this

value={this.state.fields && this.state.fields["name"] || ''}

work for me.

But I set initial state like this:

this.state = {
  fields: [],
}
Hypaethral answered 14/3, 2021 at 16:16 Comment(0)
H
4

I came across the same warning using react hooks, Although I had already initialized the initial state before as:-

const [post,setPost] = useState({title:"",body:""})

But later I was overriding a part of the predefined state object on the onChange event handler,

 const onChange=(e)=>{
        setPost({[e.target.name]:e.target.value})
    }

Solution I solved this by coping first the whole object of the previous state(by using spread operators) then editing on top of it,

 const onChange=(e)=>{
        setPost({...post,[e.target.name]:e.target.value})
    }
Hollyanne answered 23/5, 2021 at 5:14 Comment(0)
P
4

The problem occurs even if you set undefined to the value at a previous rendering that happened even before initializing things properly.

The issue went by replacing

value={value}

with

value={(value==undefined?null:value)}
Presnell answered 28/10, 2022 at 1:52 Comment(0)
G
3

Warning: A component is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.

Solution : Check if value is not undefined

React / Formik / Bootstrap / TypeScript

example :

{ values?.purchaseObligation.remainingYear ?
  <Input
   tag={Field}
   name="purchaseObligation.remainingYear"
   type="text"
   component="input"
  /> : null
}
Gladiate answered 12/5, 2020 at 14:59 Comment(0)
E
3

The reason of this problem when input field value is undefined then throw the warning from react. If you create one changeHandler for multiple input field and you want to change state with changeHandler then you need to assign previous value using by spread operator. As like my code here.

constructor(props){
    super(props)
    this.state = {
        user:{
            email:'',
            password:''
        }
    }
}

// This handler work for every input field
changeHandler = event=>{
    // Dynamically Update State when change input value
    this.setState({
        user:{
            ...this.state.user,
            [event.target.name]:event.target.value
        }
    })
}

submitHandler = event=>{
    event.preventDefault()

    // Your Code Here...
}

render(){
    return (
        <div className="mt-5">
       
            <form onSubmit={this.submitHandler}>
                <input type="text" value={this.state.user.email} name="email" onChage={this.changeHandler} />
                
                <input type="password" value={this.state.user.password} name="password" onChage={this.changeHandler} />

                <button type="submit">Login</button>
            </form>
      

        </div>
    )
}
Elviaelvie answered 15/8, 2020 at 18:0 Comment(0)
E
2

Multiple Approch can be applied:

  • Class Based Approch: use local state and define existing field with default value:
constructor(props) {
    super(props);
    this.state = {
      value:''
    }
  }
<input type='text'
                  name='firstName'
                  value={this.state.value}
                  className="col-12"
                  onChange={this.onChange}
                  placeholder='Enter First name' />

  • Using Hooks React > 16.8 in functional style components:
[value, setValue] = useState('');
<input type='text'
                  name='firstName'
                  value={value}
                  className="col-12"
                  onChange={this.onChange}
                  placeholder='Enter First name' />

  • If Using propTypes and providing Default Value for propTypes in case of HOC component in functional style.
 HOC.propTypes = {
    value       : PropTypes.string
  }
  HOC.efaultProps = {
    value: ''
  }

function HOC (){

  return (<input type='text'
                  name='firstName'
                  value={this.props.value}
                  className="col-12"
                  onChange={this.onChange}
                  placeholder='Enter First name' />)

}


Elaboration answered 15/7, 2020 at 22:16 Comment(0)
T
2

Change this

  const [values, setValues] = useState({intialStateValues});

for this

  const [values, setValues] = useState(intialStateValues);
Twenty answered 4/10, 2020 at 18:53 Comment(0)
G
2

I also faced the same issue. The solution in my case was I missed adding 'name' attribute to the element.

<div className="col-12">
   <label htmlFor="username" className="form-label">Username</label>
        <div className="input-group has-validation">
             <span className="input-group-text">@</span>
                  <input 
                      type="text" 
                      className="form-control" 
                      id="username"
                      placeholder="Username" 
                      required=""
                      value={values.username}
                      onChange={handleChange}
                  />
                  <div className="invalid-feedback">
                      Your username is required.
                  </div>
        </div>
</div>

After I introduced name = username in the input list of attributes it worked fine.

Gerber answered 24/1, 2022 at 15:49 Comment(0)
B
2

For functional component:

const SignIn = () => {

  const [formData, setFormData] = useState({
    email: "",
    password: ""
  });

  
  const handleChange = (event) => {
    const { value, name } = event.target;
    setFormData({...formData, [name]: value });
  };


  const handleSubmit = (e) => {
    e.preventDefault();
    console.log("Signed in");
    setFormData({
      email: "",
      password: ""
    });
  };


  return (
    <div className="sign-in-container">
      <form onSubmit={handleSubmit}>
        <FormInput
          name="email"
          type="email"
          value={formData.email}
          handleChange={handleChange}
          label="email"
          required
        />
        <FormInput
          name="password"
          type="password"
          value={formData.password}
          handleChange={handleChange}
          label="password"
          required
        />
        <CustomButton type="submit">Sign in</CustomButton>
      </form>
    </div>
  );
};

export default SignIn;
Brinkman answered 31/1, 2022 at 9:23 Comment(0)
G
2

While this might sound crazy, the thing that fixed this issue for me was to add an extra div. A portion of the code as an example:

  ... [Other code] ...
  const [brokerLink, setBrokerLink] = useState('');
  ... [Other code] ...

  return (
    ... [Other code] ...
              <div styleName="advanced-form" style={{ margin: '0 auto', }}>
                {/* NOTE: This div */}
                <div>
                  <div styleName="form-field">
                    <div>Broker Link</div>
                    <input
                      type="text"
                      name="brokerLink"
                      value={brokerLink}
                      placeholder=""
                      onChange={e => setBrokerLink(e.target.value)}
                    />
                  </div>
                </div>
              </div>
    ... [Other code] ...
  );
... [Other code] ...

Was very strange. Without this extra div, it seems react initially rendered the input element with no value attribute but with an empty style attribute for some reason. You could see that by looking at the html. And this led to the console warning..

What was even weirder was that adding a default value that is not an empty string or doing something like value={brokerLink || ''} would result in the exact same problem..

Another weird thing was I had 30 other elements that were almost exactly the same but did not cause this problem. Only difference was this brokerLink one did not have that outer div.. And moving it to other parts of the code without changing anything removed the warning for some reason..

Probably close to impossible to replicate without my exact code. If this is not a bug in react or something, I don't know what is.

Goldman answered 24/2, 2022 at 0:35 Comment(0)
C
1

For me, this was the mistake:

<input onChange={onClickUpdateAnswer} value={answer.text}>
  {answer.text}
</input>

As you see, I have passes string into the body of the Input tag,

Fix:

<input onChange={onClickUpdateAnswer} value={answer.text}></input>
Craggie answered 10/9, 2022 at 17:41 Comment(0)
S
0

Change value to defaultValue and it will resolve the error in react or in any other framework.

           <input
            // value={mainOrder.property1}
            defaultValue = {mainOrder.property1}
            onChange={(e) =>
              setMainOrder({ ...mainOrder, property1: e.target.value })
            }
          />
Sleazy answered 9/3, 2023 at 15:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.