A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined

11,667

Issues

  1. React uses Synthetic Events that are quickly nullified and returned back to the event pool. By the time the state update is processed the event object has been nullified and your state values set to undefined. This causes the controlled to uncontrolled input warning.

  2. The other issue is not correctly shallow copying the existing nested contactUs state when new values arrive for other inputs. React's this.setState only merges root level state, all deeper state should be manually merged.

Solution

Save the input value before enqueueing the state update. Use a functional state update to access the previous state.

handleFirstNameChange = (event) => {
  const { value } = event.target;
  this.setState(prevState => ({
    contactUs: {
      ...prevState.contactUs, // <-- copy other nested state
      name: value,
    },
  }));
};

Suggestion

To make your code a bit more DRY you can define a single onChange handler to handle all the inputs.

handleChange = (event) => {
  const { id, value } = event.target;
  this.setState((prevState) => ({
    contactUs: {
      ...prevState.contactUs,
      [id]: value
    }
  }));
};

The inputs. Ensure all the id attributes match their react state counterpart. Use the same handler for each.

<div className="form-element">
  <label>Name:</label>
  <input
    type="text"
    id="name"
    value={this.state.contactUs.name}
    onChange={this.handleChange}
  />
</div>
<div className="form-element">
  <label>Email:</label>
  <input
    type="email"
    id="emailAddress"
    value={this.state.contactUs.emailAddress}
    onChange={this.handleChange}
  />
</div>
<div className="form-element">
  <label>Telephone:</label>
  <input
    type="text"
    id="telephone"
    value={this.state.contactUs.telephone}
    onChange={this.handleChange}
  />
</div>

Edit a-component-is-changing-a-controlled-input-to-be-uncontrolled-this-is-likely-ca

Share:
11,667

Related videos on Youtube

Author by

wiki

Updated on June 04, 2022

Comments

  • wiki 7 months

    This is my ContactUs component

    import React, { Component } from "react";
    import { Button } from "react-bootstrap";
    class ContactUs extends Component {
      constructor(props) {
        super(props);
        this.state = {
          contactUs: {
            telephone: "",
            name: "",
            emailAddress: "",
          },
        };
      }
      render() {
        return (
          <div>
            <h2>Contact Us</h2>
        <div className="form-element">
              <label>Name:</label>
              <input
                type="text"
                id="name"
                value={this.state.contactUs.name}
                onChange={this.handleFirstNameChange}
              />
            </div>
            <div className="form-element">
              <label>Email:</label>
              <input
                type="email"
                id="email"
                value={this.state.contactUs.emailAddress}
                onChange={this.handleEmailChange}
              />
            </div>
            <div className="form-element">
              <label>Telephone:</label>
              <input
                type="text"
                id="telephone"
                value={this.state.contactUs.telephone}
                onChange={this.handletelephoneChange}
              />
            </div>
            <Button onClick={this.sendEmail}>Send Email</Button>
          </div>
        );
      }
      handleFirstNameChange = (event) => {
        this.setState({ contactUs: { name: event.target.value } });
      };
      handletelephoneChange = (event) => {
        this.setState({ contactUs: { telephone: event.target.value } });
      };
      handleEmailChange = (event) => {
        this.setState({ contactUs: { emailAddress: event.target.value } });
      };
     sendEmail = () => {
        console.log("Name is " + this.state.contactUs.name);
        console.log("Email address is " + this.state.contactUs.emailAddress);
        console.log("Telephone is " + this.state.contactUs.telephone);
      };
    }
    export default ContactUs;
    

    These are questions related to above component.

    1. When I add something to one of three elements in the component, I received following error. How can I get rid of the error?

    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. More info: https://reactjs.org/link/controlled-components

    1. After I filled Name, telephone and email and press the button, I received following output.

    Name is undefined
    Email address is undefined
    Telephone is Test Telephone

    Why the output does not contain the values for Name and email address?

    Thanks in advance.

    • Rohit Khanna almost 2 years
      Writing this this.setState({ contactUs: { telephone: event.target.value } }) overrides existing contactUs state completely as contactUs is an object, Replace this by this.setState({ contactUs: {...this.state.contactUs, telephone: event.target.value } }) so that you donot get undefined in final values.