DEV Community

Cover image for Managing Form state with React useState hook 🪝.
Barkley Santo
Barkley Santo

Posted on

Managing Form state with React useState hook 🪝.

Est. reading time: 8 mins


If you're starting to work with React, you'll come to learn about State. If you're not quite familiar with state yet, we can summarize state as being a variable that stores data/information within your React component, this data can be updated/changed as users interact with your app.

To help us manage state in our component, React gives us the State Hook, which we can use to store and set that data.

We'll be working on the code for this simple form with two inputs, first and last name.

code 1

Let's check out an example to try and get caught up to speed. Take a quick look at this code here, and I'll try to explain below.

//React Code

export default function Form() {
    const [firstName, setFirstName] = React.useState("")
    const [lastName, setLastName] = React.useState("")

    function handleFirstNameChange(event) {
        setFirstName(event.target.value)
    }

    function handleLastNameChange(event) {
        setLastName(event.target.value)
    }

    return (
        <form>
            <input
                type="text"
                placeholder="First Name"
                onChange={handleFirstNameChange}
            />
            <input
                type="text"
                placeholder="Last Name"
                onChange={handleLastNameChange}
            />
        </form>
    )
}
Enter fullscreen mode Exit fullscreen mode

At the top of the React code you'll see we use the useState() hook two times.

Once to change the state of the First Name input and another for the Last Name. We are storing our state (data) for the First Name in a variable called firstName, and then we use the setFirstName function to update what's stored in it.

When we initialize state for both firstName and lastName variables, you'll see we're initializing state to equal an empty string value with useState("").

Just below that, you'll see two other functions, handleFirstNameChange and handleLastNameChange. Each <input/> element below that, has an onChange property which listens for a change in that particular element and it executes the function it's holding as its value.

In short, the function that will run when there are any changes to our first name input, is handleFirstNameChange. The Last Name input has its own handleLastNameChange.

This code works really well 😊! When the First Name input is updated, its respective function will execute; the neat thing is that when the onChange is triggered, it automagically passes an event object to our handler function. If you take a look at each change handler, you see that they accept an event parameter.

We can break down that event to see the changes, by looking at event.target.value (line 8 where we log the updated value) here's what it looks like below.

code gif1

Though it works, this solution is not scalable. Imagine you have a form with 10+ inputs. It's not very practical to write a handleChange function for each and every input on the page.

In programming, we try to keep our code as DRY 🌵 (Don't Repeat Yourself) as possible. So, let's fix this to ensure we aren't duplicating code we've already written.

Check out the refactored code below!

export default function Form() {
    const [formData, setFormData] = React.useState(
        {firstName: "", lastName: ""})

    function handleChange(event) {
        setFormData(prevState => {
            return{
                ...prevState,
                [event.target.name]: event.target.value
            }
        })
    }
    console.log(formData)
    return (
        <form>
            <input
                type="text"
                placeholder="First Name"
                onChange={handleChange}
                name="firstName"
            />
            <input
                type="text"
                placeholder="Last Name"
                onChange={handleChange}
                name="lastName"
            />
        </form>
    )
}

Enter fullscreen mode Exit fullscreen mode

Let's make note of a few major differences.

  1. There is only one handleChange function now. We got rid of handleLastnameChange and handleFirstNameChange.

  2. We added a name property to our inputs. One input has a name value of firstName and the other has lastName. Keep this in mind for the cherry on top 🍒!

3.) Our initial state at the top is named differently, as well as the state change function. (They're renamed so we know they're more 'general'. The names have nothing to do with their behavior)

4.) Our handleChange function looks a bit different (see below).

    function handleChange(event) {
        setFormData(prevState => {
            return{
                ...prevState,
                [event.target.name]: event.target.value
            }
        })
    }
Enter fullscreen mode Exit fullscreen mode

This way we can handle changes for both inputs with a single function. We call setFormData (our newly named state function). The useState hook gives us access to previous state which we can use to update the current state with the new one!

If we console.log() prevState now (blue arrow), you'll see it logs the most recent state value, in this case, it's the initialized state that we saved in formData (green).

Previous State

Cool, huh?

Now, this last part is the cherry on top 🍒. The handleChange function is going to return an updated state (an object type).

Here its return statement:

            return{
                ...prevState,
                [event.target.name]: event.target.value
            }
Enter fullscreen mode Exit fullscreen mode

We're using the spread operator (it's the 3 dots), to make a copy of our prevState object and then after that comma, we are updating the [event.target.name]. Remember that name property we added to our inputs? This is telling the handleChange to return the previous state BUT, to update this particular name property to be equal to the value of the target (input element) that received the event.

So it's saying; if the input with name="firstName" is targeted, let's take the previous state of this component, and update that key (name) with the new value.

And there you have it! Not so short, but certainly a powerful tool that React gives us to keep our code clean and more maintainable.

If you have any other feedback, please feel free to share! I'm always happy to learn more about how to improve my work 🤘🏾.

Here's a puppyPuppy

Top comments (0)