Building Controlled Forms Using Functional Components in React

Sonya Vera
The Startup
Published in
4 min readNov 11, 2020

If you’re like me, you graduated from the Flatiron School or another bootcamp thinking functional components in React were used only if you didn’t need to maintain state in that component or write any complex functions. And even in those cases, you might still use class components because it’s all you were really taught during bootcamp.

Another possible scenario is that you didn’t graduate from any bootcamp, you weren’t under any type of impression about functional components vs. class components, and you’d just like to learn about exactly what I stated in the title.

Here we go!

Functional components have some advantages over class components you might not have considered in real-world applications:

  1. Not having to use “this.” Refactoring and debugging become quite a bit simpler in complex applications when you no longer have to worry about keeping track of “this.”
  2. Fewer lines of code. In a functional component, you don’t need to wrap your JSX inside of a render() function. If there’s only one component it makes a difference of only 2 lines, but in a real-world application where there might be hundreds or thousands of components, this can make a big difference.

In case nobody’s lectured you yet, controlled forms are much more useful than uncontrolled forms. Using controlled forms, the value of an input is set by a value being held in the state. The state is updated each time the user makes a change in one of the form’s inputs, not just when the user presses a ‘submit’ button. As a result, forms can become dynamic.

Imagine your user is creating an account and the bottom of the form has a “Sign Up” button. Perhaps you want this button to be disabled until all fields are filled, or until the password contains at least 8 characters. Controlled forms are the way to accomplish this!

So let’s build a basic chore chart React app using only functional components where you and your roommates can log the chores you do around the house.

Check out the diagram to the left to see the basic structure of the app.

Users will fill out a form in the Chore Form component to log the chore they completed. This chore log needs to get passed up to the App component so that it can be passed to the Chore Chart component where the chore log will be rendered in a table. So if we want this form to be controlled — which it should be — we know we’ll want to use State in the chore form component as the single source of truth for chore logs.

Let’s start by building out the functional component for the chore form.

Here’s the skeleton of the component, before we implement state or the main features of a controlled form:

Next, we’ll import the { useState } hook from React so that we can begin to store state in this functional component.

The basic syntax for using the useState hook is as follows:

const [state, setState] = useState();

On the left hand side, ‘state’ is the name of the state object, and ‘setState’ is the function you’ll use to set the state of this object (ex. setCount if the state object is ‘count’) . On the right hand side, useState() can be set to the initial value of the state (ex. useState(0) if you want the initial value to be 0).

So for our Chore Form function, let’s add the following code above the return() and outside of the handleSubmit function:

const [choreDesc, setChoreDesc] = useState();const [name, setName] = useState();const [date, setDate] = useState();

For each individual input, let’s add an onChange event to update state on each user input using the useState hook function. We’ll need to pass it the event target, the value of which contains the user’s input information. Let’s look at the input for chore description as an example:

<inputname='choreDesc'type='text'onChange={e => setChoreDesc(e.target.value)}/>

Now, make sure each input has a value property that is set equal to its corresponding state object. To the above, we add the following line somewhere between the opening and closing tags for ‘input’:

value={choreDesc}

Now let’s get our App component set up.

We’ll need the following:

  1. A state object belonging to App which stores all of the chore logs and can be passed down to the chore chart as a prop.
  2. A function that accepts a new chore log and adds it to the state. This function will be passed down to the chore form component as a prop.

Check it out:

Looks good!

Now back to our chore form component.

We have two jobs:

  1. Use destructuring to neatly access the addChoreLog function that was passed in through props by the App component:
function ChoreForm({ addChoreLog }){

2. Pass the state objects to the addChoreLog function inside handleSubmit, which is invoked when the user presses the “Add Log” button for the form.

addChoreLog([choreDesc, name, date])

Here’s the final version of the chore form component so you can see it all together:

Final step! Back to the chore chart component.

Be sure to map across the list of chore logs and create a table row for each new log that is generated. Use curly braces within the return to write a map function in JavaScript. See the completed chore chart component:

And there you have it!

If you’ve written controlled forms using class components before, you’ll be familiar with most of the concepts I mentioned here. But it’s definitely taken me some practice to get used to writing these functional components at the same speed as I can class components.

I’ll keep practicing, as I’ve already experienced some of the benefits of using functional components and I hear they’re used more widely in the professional world as well.

--

--

Sonya Vera
The Startup

I’m a software engineering student at the Flatiron school in NYC.