React Forms

Where does the data go?

There are various approaches to handling forms, but mostly they boil down to one decision: where do you want to keep the form data?

  • in component state (controlled components)
  • in the DOM, using refs (uncontrolled components)

Generally it is preferred to handle form data with controlled components rather than using an uncontrolled form. Uncontrolled forms can make it harder to manage and validate form state, especially in more complex scenarios. It is therefore recommended to use controlled components as they provide more structured and manageable ways to handle data in forms.

Controlled components

Here's an example showing a form that is using controlled components:

const [formData, setFormData] = useState({
name: '',
description: '',
})
return (
<form onSubmit={handleSubmit}>
<label htmlFor="name">Name</label>
<input type="text"
name="name"
value={formData.name}
onChange={handleChange}
/>
<label htmlFor="description">Description</label>
<textarea
name="description"
value={formData.description}
onChange={handleChange}
/>
<input type="submit" value="Add" />
</form>
)

Notice that this has a value prop on each form element. This means that whatever is in the component state at that location will also be shown in the form.

Change handlers

Each input also has an onChange prop which calls the handleChange function each time the user types a key. Take a look at how we could define it:

const handleChange = (evt) => {
const field = evt.target.name
setFormData({
...formData,
[field]: evt.target.value
})
}

If you haven't seen this sort of thing before, it's called computed property name syntax. Whatever string the variable field contains will become the property name.

Submit handlers

The submit handler will be called when the user submits the form. As seen below, it first needs to prevent the default behaviour of the form (as we do not want it making requests to the server) before performing whatever action is required with the collected form data. Once done, we may also choose to reset the state to empty values (as below).

const handleSubmit = (evt) => {
evt.preventDefault()
// doSomething(formData)
setFormData({
name: '',
description: ''
})
}

What about validation? Well, we could check each property and make sure it wasn't empty:

const { name, description } = formData
if (name.length && description.length) {
doSomething(formData)
}

Once it gets much more complex than that, you'll need to decide whether to continue writing your own form logic or relying on an existing library to do it for you.