I'm creating a simple form with react using controlled inputs. In my component state, I have 2 properties "clientName" and "license" on state. changing those works fine. But there's a "shipping" property that is an object. Changing any of the shipping properties gives an error. For example, if i change "address1", as soon as the state is set in the handleShippingChange function, I get the error:
Warning: TextField is changing a controlled input of type text to be
uncontrolled. Input elements should not switch from controlled to
uncontrolled (or vice versa). Decide between using a controlled or
uncontrolled input element for the lifetime of the component.
I suspect it has something to do with how I'm defining the value for those TextFields and how I'm setting the state for shipping properties. What am I doing wrong? The code for my component is below:
import React, {Component} from 'react';
import TextField from 'material-ui/TextField';
import RaisedButton from 'material-ui/RaisedButton';
import 'whatwg-fetch';
class Clients extends Component {
constructor() {
super();
this.state = {
"clientName": "client name",
"shipping": {
"name": "name",
"address1": "address 1",
"address2": "address 2",
"city": "city",
"state": "state",
"zip": "zip",
"country": "country"
},
"license": "license"
};
this.handleChange = this.handleChange.bind(this);
this.handleShippingChange = this.handleShippingChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
};
handleChange(event) {
this.setState({
[event.target.name]: this.getFieldValue(event.target)
});
};
handleShippingChange(event) {
this.setState({
shipping: {
[event.target.name]: this.getFieldValue(event.target)
}
});
};
getFieldValue(target) {
return target.type === 'checkbox' ? target.checked : target.value;
};
handleSubmit = (event) => {
event.preventDefault();
// do some stuff
};
render() {
return <div>
<h1>
Clients Page
</h1>
<form onSubmit={this.handleSubmit}>
<TextField
hintText="Enter the client name"
floatingLabelText="Client Name"
value={this.state.clientName}
onChange={this.handleChange}
name="clientName"
/>
<h2>Shipping Info</h2>
<TextField
hintText=""
floatingLabelText="Name"
value={this.state.shipping.name}
onChange={this.handleShippingChange}
name="name"
/>
<br />
<TextField
hintText=""
floatingLabelText="Address Line 1"
value={this.state.shipping.address1}
onChange={this.handleShippingChange}
name="address1"
/>
<br />
<TextField
hintText=""
floatingLabelText="Address Line 2"
value={this.state.shipping.address2}
onChange={this.handleShippingChange}
name="address2"
/>
<br />
<TextField
hintText=""
floatingLabelText="City"
value={this.state.shipping.city}
onChange={this.handleShippingChange}
name="city"
/>
<br />
<TextField
hintText=""
floatingLabelText="State"
value={this.state.shipping.state}
onChange={this.handleShippingChange}
name="state"
/>
<br />
<TextField
hintText=""
floatingLabelText="Zip Code"
value={this.state.shipping.zip}
onChange={this.handleShippingChange}
name="zip"
/>
<br />
<TextField
hintText=""
floatingLabelText="Country"
value={this.state.shipping.country}
onChange={this.handleShippingChange}
name="country"
/>
<br />
<TextField
hintText=""
floatingLabelText="License"
value={this.state.license}
onChange={this.handleChange}
name="license"
/>
<br />
<RaisedButton label="OK" primary={true} type="submit" />
</form>
</div>
};
}
export default Clients;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Best Answer
Controlled/Uncontrolled input means if the
<input>
field has a value or not.The idea is that you don't want to change from a controlled to an uncontrolled input. Both types of input acts differently and this could potentially lead to bugs and/or inconsistency.
The easiest fix is to ensure there's always a default value (in the case of an empty field, the default would be an empty string
''
).Also, note that consistent types are usually better than nullable types given you have a guarantee on the type of a certain value. This helps a lot in reducing overhead due to null checks (
if (val != null) { /* ...etc */ }
)But if you just want a one line fix, you can also provide the default value inline in jsx: