A controlled form is used to submit data to be persisted to a database that derives its input value from state
. The alternative to a controlled form is an uncontrolled form, in which the data is handled not by state
but, instead, by the DOM. Simply put, in a controlled form, state
is aware of the characters being typed into any given form field at any given time. If a controlled form had a parenting style, it would be a helicopter parent, where an uncontrolled form would be a mega-chill and sort of 'hands off' parent, not totally paying attention but still kinda' there.
One good way to tell the difference between a controlled form or uncontrolled form, is to check for value
or for defaultValue
. If the component has a value
prop
, it is a controlled component. If it does not have a value
prop
, it is an uncontrolled component. An uncontrolled component can optionally have a defaultValue
prop
set to its initial value -- but -- if the component has a value
prop
, it is a controlled component.
How does it work, you ask? Let's walk through. I arrive at a browser window which is rendering a controlled form. It wants my first name.
The current state
looks like this
state = {
name: '',
}
I start to type my name, I hit the letter 'M' on my keyboard. Now, state
looks like this
state = {
name: 'M',
}
I continue typing my name, but then I accidentally hit the wrong key. state
looks like this now
state = {
name: 'Mau',
}
I realize my error. state
looks like this,
state = {
name: 'Ma',
}
and I correct my mistake. state
looks like this.
state = {
name: 'May',
}
Before I even press the 'submit' button on the form, state
already knows my name. That's accomplished through the following event handler
handleChange = e => {
const { name, value } = e.target
this.setState({
[name]: value
})
}
Which is invoked inside of the controlled form by the event listener on line five
<form onSubmit={this.handleSubmit}>
<label>your name:</label>
<input type="text">
value={this.state.name}
onChange={this.handleChange}
name="name"/>
<br/>
<input type="submit" value="submit"/>
</form>
These guys work together and say "as soon as there is a change in the form field, we're going to handle that change by passing and destructuring the data provided in the form field and setting it equal to the current state
".
Pretty cool.
Now that I've entered my name, I'm ready to hit the 'submit' button. Here we have another event listener & handler ready to send the data from the form field to the database and reset state
handleSubmit = e => {
e.preventDefault();
this.props.addPerson(this.state)
this.setState({
name: ''
})
}
This event handler is invoked inside of the controlled form when I press the 'submit' button. My name is then couriered to the database, and state
looks like this again now, just like it did at the beginning
state = {
name: '',
}
Why do we do it this way? Controlling forms makes it more convenient to share form values between components. Since the form values are stored in state
, they are easily passed down as props
or sent upward via a function supplied in props
. Another reason controlled forms are preferred is that uncontrolled forms typically yield code that is not DRY. In an uncontrolled form, one may access the form data within the event -- that is to say, the data is available
handleSubmit = e => {
e.preventDefault()
const carMake = e.target.children[0].value
const carModel = e.target.children[1].value
this.persistDataToDatabase({ carMake, carModel
})
}
but, it is just not quite as efficient, or elegant as the alternative, a controlled component.
handleSubmit = e => {
e.preventDefault()
this.persistDataToDatabase(this.state)
}
Thanks for reading!
Top comments (0)