0

Today I was just trying to create a form for login/signup with functional components and useState hook with React. However, I ran into this issue. If anyone can help, it would be greatly appreciated. I tried searching for a reason, however, it was to no avail. Thanks.

  • As soon as I provide input into the email field, I receive a warning. ''Warning: A component is changing a controlled input of type undefined to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). '' I do not know where my code is going wrong for this.
function Login(){
   const [state, setState] = useState({
       email:'',
       password:'',
   })

   const handleChange = e => {
       setState({ [e.target.name]: e.target.value })
   } 

   const handleSubmit = e => {
       e.preventDefault();
       var email = state.email;
       var password = state.password;

       // Firebase requires passwords to be of length 6 or above
       if (password.length < 6) {
           alert("Password must of length 6 or more.");
       } else {
           fire.auth().createUserWithEmailAndPassword(email, password); 
           
           alert("Signed Up Successfully!");
       }
   }

   return(
       <>
       <Navbar/>
       <div>
           <form>
               <input name="email" value={state.email} onChange={handleChange} placeholder="email"></input>
               <input name="password" value={state.password} onChange={handleChange} placeholder="password"></input>
               <button type="submit" onClick={handleSubmit}>Sign Up!</button>
               <button type="submit">Login!</button>
           </form>
       </div>
       </>
   );
}

2 Answers 2

2

In useState hook the old state and the object you pass to the setState function is not automatically merged like the setState of class components. Here you need to ensure all state properties are present when you call setState along with the updated properties of the state.

Otherwise the object you pass to the setState will replace object in the current state.

Your state has two properties:

{
   email:'',
   password:'',
} 

But when you update the state in the handleChange you are omitting one state property. The input which is associated with the missing property goes from being a controlled to an uncontrolled component and hence you get the error:

const handleChange = e => {
   setState({ [e.target.name]: e.target.value })
} 

Either you need to merge the old state with the updated property:

const handleChange = e => {
   setState({ ...state, [e.target.name]: e.target.value })
} 

Or split the state into two useState calls one for each property:

const [email, setEmail] = useState('');
const [password, setPassword] = useState('');

Then have handlers to update for each:

const handleEmailChange = e => {
  setEmail({ [e.target.name]: e.target.value })
}

const handlePasswordChange = e => {
  setPassword({ [e.target.name]: e.target.value })
}
Sign up to request clarification or add additional context in comments.

6 Comments

Thank you it makes sense. However, it is possible for you to elaborate on what ...state actually does. I am still learning React so if you dont mind!!
I like separating the email and password, but i guess the best practice should be to use ...state instead if I am not wrong!
@TanDev, you might have thought that the setState you got from the useState call behaved like the setState of class components, this is not the case. The object you pass to the setState from your hook replaces the old state completely, so in your case the one property which is updated stays in the state and the other one is removed when you update the state. Hence the input associated with the missing property becomes uncontrolled from being controlled
Yes I understand that now. Thanks for the clear explanation. But I am still wondering, is '...state' in the setState just responsible for merging the state? If so how does that exactly happen.
@TanDev that is the object spread operator. Basically ... before an object spreads the object into the individual properties and then you provide the updated property so the new resulting object has all the previous properties and also the new updated one
|
1

useState updates the entire object, rather than the specific value, Unlike setState in class componenets. So you need to destructor the exising value of state and add the updated value, Please find the handle change code updated below:

function Login(){
   const [state, setState] = useState({
       email:'',
       password:'',
   })

   const handleChange = e => {
       setState({ 
                 ...state,
                [e.target.name]: e.target.value })

   } 

   const handleSubmit = e => {
       e.preventDefault();
       var email = state.email;
       var password = state.password;

       // Firebase requires passwords to be of length 6 or above
       if (password.length < 6) {
           alert("Password must of length 6 or more.");
       } else {
           fire.auth().createUserWithEmailAndPassword(email, password); 
           
           alert("Signed Up Successfully!");
       }
   }

   return(
       <>
       <Navbar/>
       <div>
           <form>
               <input name="email" value={state.email} onChange={handleChange} placeholder="email"></input>
               <input name="password" value={state.password} onChange={handleChange} placeholder="password"></input>
               <button type="submit" onClick={handleSubmit}>Sign Up!</button>
               <button type="submit">Login!</button>
           </form>
       </div>
       </>
   );
}

1 Comment

Thank you it makes sense. However, it is possible for you to elaborate on what ...state actually does. I am still learning React so if you dont mind!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.