Controlled Components in React

Sid Jagarlamudi
3 min readSep 12, 2020

React makes the code we write easier to manage, easier to re-use, and easier to make sense of. When it comes to writing forms, we shouldn’t have to deviate from that structure. React allows for uncontrolled and controlled components. Without using this framework, when we intend to grab data from our forms, we do it in an uncontrolled fashion. It requires lots of DOM node traversal which can be rather time consuming if our form has several sections to fill out. Let’s check out an uncontrolled component in React.

handleSubmit = (event) => {
event.preventDefault()
const favSnack = event.target.children[0].value
const favMovie = event.target.children[1].value
const favMusic = event.target.children[2].value
this.doSomethingWithFormData({ favSnack, favMovie, favMusic })
}<form onSubmit={ event => this.handleSubmit(event) }>
...
</form>

This works very similarly to an HTML form. Once the form is submitted, the handleSubmit function is invoked. This handles all the data passed into the form by grabbing it and sending it to another function that deals with what we want done with the data. The issue arises when our form becomes quite extensive. for each field, we would have to traverse the DOM nodes and grab it which is not only time consuming, but takes up several lines of code. There must be a better way to do this. Well in fact there is! Let me introduce you to controlled components.

When we use controlled components, whatever the value of the component is, will be stored in the state. In our form example, the value can then be updated according to the user response and the state changes and re-renders subsequently. With each character that is typed into the form, the state is updated and changed to match whatever lives in the form. One could say it’s very reactive… All this is to say that our new handleEvent function would look something like this no matter how big our form gets.

handleSubmit = (event) => {
event.preventDefault()
this.doSomethingWithFormData(this.state)
}

To get a full picture, our new form would look something like this.

Even with how structured this new form component is, you can imagine that with each input field provided, we would need a matching handle function to go with it. There is a way we can abstract all these handle functions into one handleFormData function. Let’s try it! Since we have event being passed down as an argument, we have access to its attributes like event.target. Giving our input a name attribute, we would be able to call event.target.name to access each node.

<input type=”text” name=”favFood” value={this.state.favFood} />
<input type=”text” name=”favHobby” value={this.state.favHobby} />
<input type=”text” name=”favMusic” value={this.state.favMusic} />

With this form setup, as long as our name attribute matches up to the keys in our state, we can write out one function for all our form data. This is because although the name attribute is the same across all input fields, the value is different as it relates to the individual state of each input.

handleFormData = event => {
this.setState({
[event.target.name]: event.target.value
})
}

Nice! You are now able to extract data from components in a controlled manner. This will allow for data transferring to be much smoother. Give it a try, and happy coding!

--

--