0

I have the following component where I display user's existing details in the inputs as values and user should be able to change those details and click save. But the issue is that it shows the following error in the console:

  **index.js:1 Warning: A component is changing a controlled input of type text to be uncontrolled.**

Here is the component code:

const Settings = (props) => {
  const { createUser, isAuthenticated, history, errors } = props;
  const dispatch = useDispatch();
  const authUser = useSelector(state => state.auth.user);

  const [user, setUser] = useState({
    data: {
      name: "",
      email: "",
      password: "",
    },
  });

  const [error, setError] = useState({
    nameError: "",
    emailError: "",
    passwordError: "",
  });

  useEffect(() => {
    setUser({
      data: {
        email:authUser.email,
      }
    });    
  }, [authUser]);


  const { name, email, password } = user.data;
  const { nameError, emailError, passwordError } = error;

  const onUpdateUser = (e) => {
    e.preventDefault();

    const isValid = formValidator(user.data, setError);

    if (isValid) {
      dispatch(updateUser(user.data))
    }
  };

  const onChange = (e) => {
    const { name, value } = e.target;
    const { data } = user;
    setUser({
      data: {
        ...data,
        [name]: value,
      },
    });
  };

  return (
    <BForm title="Create an account" handleSubmit={onUpdateUser}>
      {errors ? <p className="error-feedback">{errors}</p> : ""}
      <BInput
        name="name"
        type="text"
        handleChange={onChange}
        value={name}
        placeholder="Your Name"
        error={nameError}
        
      />
      <BInput
        name="email"
        type="email"
        handleChange={onChange}
        value={email}
        placeholder="Email"
        error={emailError}
        required
      />
      <BInput
        name="password"
        type="password"
        value={password}
        handleChange={onChange}
        placeholder="Password"
        error={passwordError}
        required
      />
      <div className="buttons">
        <BButton customClass="login-btn" isfullwidth={true} type="submit">
          {" "}
          Save{" "}
        </BButton>
      </div>
    </BForm>
  );
};

export default Settings;

What is wrong and how can be it fixed? I need to let see the existing values and then change whatever they want.

NewTech Lover
  • 1,185
  • 4
  • 20
  • 44
  • Does this answer your question? [A component is changing an uncontrolled input of type text to be controlled error in ReactJS](https://stackoverflow.com/questions/47012169/a-component-is-changing-an-uncontrolled-input-of-type-text-to-be-controlled-erro) – secan Jun 30 '21 at 09:57
  • 1
    Check your `useEffect`, it sets `user.data` to only have `email` attribute. If you then add text to the `password` field for example, then the `onChange` handler will spread `user.data` which only contains `email` and then `password` is set by `[name]: value` leaving `name` `undefined` => a controlled input get uncontrolled...? – fast-reflexes Jun 30 '21 at 09:57
  • Indeed. It's only three fields; consider using a separate `useState` for each value. –  Jun 30 '21 at 09:59

2 Answers2

0

useState doesn't allow patching like setState did (even if it did, your data is nested at the data key so it still wouldn't work). The problem is at this line:

  useEffect(() => {
    setUser({
      data: {
        email:authUser.email,
      }
    });    
  }, [authUser]);

It needs to look like this:

  useEffect(() => {
    setUser(({data}) => ({
     data: {
      ...data,
      email:authUser.email
    }
   }))
  }, [authUser]);

Otherwise, it will remove the name and password keys and then the inputs will have undefined values (e.g. become uncontrolled).

Adam Jenkins
  • 51,445
  • 11
  • 72
  • 100
0

You problem in useEffect. Because you only update email so name and password will be lost. So the initial value of these input will show this warning. You can update like this:

  useEffect(() => {
    setUser(preState => ({
      data: {
        ...preState.data,
        email:authUser.email,
      }
    }));    
  }, [authUser]);
Viet
  • 12,133
  • 2
  • 15
  • 21