What is the error of 'A component is changing a controlled input to be uncontrolled' meaning in React and how solve it ?

May Al Sanea
By -
0




import { useState } from "react";



const Usestatetry3 = () => {

    const[name,setName] = useState({fname:"",lname:""} );
                                        
    const gettingValue = (e) =>{
            setName(e.target.value)
            console.log(e.target.value)
    }

    
    return(
        <>
            <div className="bg-blue-200 p-5 text-3xl flex-cols space-y-5 justify-center items-center">
                <div>
                    <label  className='text-red-500'>First Name
                        <input  className='ml-6' type='text'  placeholder='insert your First Name...' value={name.fname } onChange={gettingValue} />
                   </label>
                </div>
                <div>
                    <label className='mr-5 text-red-900'>Last Name
                        <input className='ml-6' type='text' placeholder='insert your Last Name...' value={name.lname } onChange={gettingValue} />
                    </label>
                </div>
                <p>input value is:<span className='text-brown-200 ml-1'>{name.fname}{name.lname}</span></p>
            </div>
        </>
    )
}

After run the above code there is warning for error in console : Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.


The error you are encountering is due to the way you are updating the name state. You need to handle the state update correctly for the individual fields (fname and lname). Here's how you can do it:

  1. Use the name state object properly.
  2. Ensure that you are updating the correct property of the state object.

Here's the corrected code:


import { useState } from "react";

const Usestatetry3 = () => {
    const [name, setName] = useState({ fname: "", lname: "" });

    const gettingValue = (e) => {
        const { name, value } = e.target;
        setName(prevState => ({
            ...prevState,
            [name]: value
        }));
        console.log({ [name]: value });
    };

    return (
        <>
            <div className="bg-blue-200 p-5 text-3xl flex-cols space-y-5 justify-center items-center">
                <div>
                    <label className='text-red-500'>First Name
                        <input
                            className='ml-6'
                            type='text'
                            name='fname'
                            placeholder='insert your First Name...'
                            value={name.fname}
                            onChange={gettingValue}
                        />
                    </label>
                </div>
                <div>
                    <label className='mr-5 text-red-900'>Last Name
                        <input
                            className='ml-6'
                            type='text'
                            name='lname'
                            placeholder='insert your Last Name...'
                            value={name.lname}
                            onChange={gettingValue}
                        />
                    </label>
                </div>
                <p>input value is:<span className='text-brown-200 ml-1'>{name.fname} {name.lname}</span></p>
            </div>
        </>
    );
};

export default Usestatetry3;
Key changes made:
  1. Added name attribute to the input fields to distinguish between fname and lname.
  2. Updated the gettingValue function to handle the changes correctly using name and value from e.target.
  3. Spread the previous state to retain the other property values when updating.

This should resolve the warning and handle the state updates correctly.

 Now i will explain the code above :

about this section :

const [name, setName] = useState({ fname: "", lname: "" });

    const gettingValue = (e) => {
        const { name, value } = e.target;
        setName(prevState => ({
            ...prevState,
            [name]: value
        }));
        console.log({ [name]: value });

In the gettingValue function, you need to handle changes for both the first name and last name inputs. The key is to know which input triggered the change event. By using e.target.name, you can identify the input (either fname or lname) and update the corresponding state property.

Here’s a breakdown of the key parts:

  1. Destructuring e.target:



  • This line extracts the name and value properties from the event target (e.target). The name corresponds to the name attribute of the input field, and the value is the current value of the input field.

  • Using setName with a callback function:

  • setName(prevState => ({
        ...prevState,
        [name]: value
    }));
    
    
    
  • prevState is the previous state value.
  • ...prevState spreads the previous state values into the new state object.
  • [name]: value dynamically updates the property (either fname or lname) with the new value.
  • This way, you can update the correct property (fname or lname) based on which input field was changed.

    If you simply used e.target.value without considering the name attribute, you wouldn't know which property to update. Here’s how it works in practice:

    Explanation with an Example:

    1. When you type in the "First Name" input:

      • e.target.name is "fname"
      • e.target.value is the new value entered
      • The state update looks like: { fname: newValue, lname: previousLnameValue }
    2. When you type in the "Last Name" input:

      • e.target.name is "lname"
      • e.target.value is the new value entered
      • The state update looks like: { fname: previousFnameValue, lname: newValue }

    Here's the relevant section of code again with added comments for clarity:


    const [name, setName] = useState({ fname: "", lname: "" });
    
    const gettingValue = (e) => {
        // Destructure name and value from e.target
        const { name, value } = e.target;
        
        // Update the state dynamically based on the input name
        setName(prevState => ({
            ...prevState, // Keep all other state properties
            [name]: value // Update the specific property based on input name
        }));
        
        // Log the updated value for debugging
        console.log({ [name]: value });
    };
    
    This approach ensures that the correct part of the state is updated based on which input field is changed, making the form handling more dynamic and robust.


    or you can use another way to solve the problen by cancle the function gettingValue and replace it with applied it directly inside each field, so the correct code will be as below:

    
    
    
    import { useState } from "react";

    const Usestatetry3 = () => {
        const [name, setName] = useState({ fname: "", lname: "" });

        return (
            <>
                <div className="bg-blue-200 p-5 text-3xl flex-cols space-y-5 justify-center items-center">
                   
                    <div>
                        <label className='text-red-500'>First Name
                            <input
                                className='ml-6'
                                type='text'
                                name='fname'
                                placeholder='insert your First Name...'
                                value={name.fname}
                                onChange={(e)=>setName(
                                                       {...name, fname:e.target.value}
                                                    )}
                            />
                        </label>
                    </div>


                    <div>
                        <label className='mr-5 text-red-900'>Last Name
                            <input
                                className='ml-6'
                                type='text'
                                name='lname'
                                placeholder='insert your Last Name...'
                                value={name.lname}
                                onChange={(e)=>setName(
                                                       {...name, lname:e.target.value}
                                                    )}
                            />
                        </label>
                    </div>


                    <p>input value is:<span className='text-brown-200 ml-1'>
                                            {name.fname} {name.lname}
                                      </span>
                    </p>
                </div>
            </>
        );
    };

    export default Usestatetry3;


    Post a Comment

    0Comments

    Post a Comment (0)