DEV Community

Cover image for How To: Controlled Forms with React
Cody Barker
Cody Barker

Posted on

How To: Controlled Forms with React

Unlike JavaScript, React makes use of state in order to keep track of component changes over time. While using state does incur some added complexity, it has its benefits, particularly when it comes to creating controlled forms. We use state to keep track of the dynamic input values in our forms. So what is state exactly?

State

While props remain static, state is dynamic data. It changes over time. Setting state allows us to rerender our components without having to refresh the page or change/add props. That's why state plays an important role in how we handle forms in React. When the input, text area, or select values of a form are set with state, we have a controlled form.

In order to use state in our app, we need to import the {useState} hook from React like so.

import React, { useState } from 'react'
Enter fullscreen mode Exit fullscreen mode

Next, we need to invoke our useState hook. The useState hook returns an array of 2 variables, the first being a reference to the value of our state variable, and the second a function that allows us to update that state, which begins with "set". Whatever is within the parentheses of the useState hook becomes the default value of our state. In many cases, it might simply be an empty string. An example of the useState hook might look a little something like this:

const [name, setName] = useState("")
Enter fullscreen mode Exit fullscreen mode

Above, "name" is our state, and "setName" is our setter function. Because we have an empty string inside the parentheses of useState, the default value of "name" is an empty string. An important note about state: whenever we want to update state, we must pass a new object or value to our setter function. By default, we don't have access to the updated state value until the component has rerendered. If we want to update state using the current value of state, we must pass it within a callback function like so.

setName((name) => `${name} Barker`)
Enter fullscreen mode Exit fullscreen mode

Finally, whenever we call set to update our state, our component and all of it's children components will rerender.

Controlled Forms

Setting up a controlled form is relatively easy. First things first, set up your form. Here's a simple example.

import React from 'react'

function Form() {
    return(
        <form>
            <input 
                type="text"
                name="name"
                placeholder="name"> 
            </input>
            <input 
                type="number"
                name="age"
                placeholder="age">
           </input>
            <button type="submit">Submit</button>
        </form>
    )
}

export default Form
Enter fullscreen mode Exit fullscreen mode

Next, import the useState hook from React.

import React, { useState } from 'react'

function Form() {
    return(
        <form>
            <input 
                type="text"
                name="name"
                placeholder="name"> 
            </input>
            <input 
                type="number"
                name="age"
                placeholder="age">
           </input>
            <button type="submit">Submit</button>
        </form>
    )
}

export default Form
Enter fullscreen mode Exit fullscreen mode

Then, call your useState hook within your component to set up state for both of your form inputs.

import React, { useState } from 'react'

function Form() {
    const [name, setName] = useState("")
    const [age, setAge] = useState("")
    return(
        <form>
            <input 
                type="text"
                name="name"
                placeholder="name"> 
            </input>
            <input 
                type="number"
                name="age"
                placeholder="age">
           </input>
            <button type="submit">Submit</button>
        </form>
    )
}

export default Form
Enter fullscreen mode Exit fullscreen mode

Next, set the input values equal to the corresponding state values. This will allow us to clear our input fields after submitting our form.

import React, { useState } from 'react'

function Form() {
    const [name, setName] = useState("")
    const [age, setAge] = useState("")
    return(
        <form>
            <input 
                type="text"
                name="name"
                placeholder="name"
                value={name}> 
            </input>
            <input 
                type="number"
                name="age"
                placeholder="age"
                value={age}>
           </input>
            <button type="submit">Submit</button>
        </form>
    )
}

export default Form
Enter fullscreen mode Exit fullscreen mode

Now we need to set up event listeners to update state and rerender our component whenever the user enters something into our input fields.

import React, { useState } from 'react'

function Form() {
    const [name, setName] = useState("")
    const [age, setAge] = useState("")
    function handleName(e) {
        setName(e.target.value)
    }
    function handleAge(e) {
        setAge(e.target.value)
    }
    return(
        <form>
            <input 
                onChange={handleName}
                type="text"
                name="name"
                placeholder="name"
                value={name}>
            </input>
            <input 
                onChange={handleAge}
                type="number" 
                name="age"
                placeholder="age"
                value={age}>
            </input>
            <button type="submit">Submit</button>
        </form>
    )
}

export default Form
Enter fullscreen mode Exit fullscreen mode

At this point, we've controlled our inputs, so from here it all depends on what we want to do when we submit. If we just wanted to add these values to an API, we could add a submit event listener to the form and make a fetch post request to the database. That might look something like this.

import React, { useState } from 'react'

function Form() {
    const [name, setName] = useState("")
    const [age, setAge] = useState("")
    function handleName(e) {
        setName(e.target.value)
    }
    function handleAge(e) {
        setAge(e.target.value)
    }
    function handleSubmit(e) {
        e.preventDefault()
        const newUser = {
            username: name,
            userAge: age
        }
        fetch('http://localhost:3001/users', {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify(newUser)
        })
        .then(r => r.json())
        .then(user => console.log(user))
        .then(
            setName(""),
            setAge("")
        )
    }

    return(
        <form onSubmit={handleSubmit}>
            <input 
                onChange={handleName}
                type="text"
                name="name"
                placeholder="name"
                value={name}>
            </input>
            <input 
                onChange={handleAge}
                type="number" 
                name="age"
                placeholder="age"
                value={age}>
            </input>
            <button type="submit">Submit</button>
        </form>
    )
}

export default Form
Enter fullscreen mode Exit fullscreen mode

To recap, whenever a user changes the text within our form input fields, the setter function is called in the event listener, updating our states by setting them equal to the input values. Every state change rerenders the component without a page refresh, updating everything immediately. How nice!

When we submit our form, we create a new object called newUser, setting the key value pairs using state.

We then make a fetch request to our example API, console.log the response which is our newUser, and then clear the input fields by setting state back to empty strings. If done correctly, our newUser will be added to the database.

Remember, we aren't just limited to controlling forms with inputs. We can also control form elements like and with state in very much the same way!

Top comments (0)