Using FormData to process form input values
Tags: webdev, javascript, form
Published: 2024-01-17 16:54:51

Before you reach out to a form library, I suggest to look at what browsers nowadays have to offer by default. In a lot of cases it will be overkill to include a form library that has implemented a ton of features that you're not going to use anyways. In this blog post I want to dive into the `FormData` instance.

## What is it?

`FormData` is an instance that you can use to create a set of key/value pairs that works seamlessly together with other native browser methods, for example:

- It can be used to directly send key/value pairs as body in `POST` requests via `fetch` or `XMLHttpRequest.send()`.
- It can be passed directly into `URLSearchParams`.

It is often used to retrieve and process data from form inputs.

## Feed it with data

Either, you manually add data to it using its `set` or `append` methods, or you connect it to a form for it to automatically create it's key/value pairs based on the form.

### Manually adding key/value pairs

To create a `FormData` instance from scratch you initialize it without passing any arguments. Then you use its `append` and/or `set` method to add data to the instance.

```javascript
const userFormData = new FormData();

// Use set to create or overwrite a value for the key passed as the first argument
formData.set('name', name);
formData.set('age', age);

preferredFruits.forEach(fruit => {
  // Use append when a key can have multiple values
  formData.append('fruit', fruit);
});
```

You are responsible for managing the data and use it to cal these methods to feed the `FormData` instance.

### Attach it to a form element

When you want the `FormData` instance to use data from a `form` element, you pass the `form` element as an argument to `FormData`.

```html
<form id="user-form">
  <input name="name" type="text" />
  <input name="age" type="number" />
  <select name="preferred-fruits" multiple>
    <option value="apple">Apple</option>
    <option value="banana">Banana</option>
    <option value="orange">Orange</option>
    <option value="grape">Grape</option>
  </select>
  <button>Submit</button>
</form>

<script>
  const form = document.getElementById("user-form");
  
  form.addEventListener("submit", () => {
    const userFormData = new FormData(form);
    
    // ...
  })
  
</script>
```

In React you use `useRef` to pass the `form` element, like so:

```tsx
function UserForm() {
  const formRef = useRef<HTMLFormElement>(null);

  return (
    <form
      ref={formRef}
      onSubmit={(event) => {
        event.preventDefault();

        // We need to check for `formRef.current`, because it can be `undefined` according to TypeScript.
        if (!formRef.current) {
          return;
        }

        const userFormData = new FormData(formRef.current);

        // ...
      }}
    >
      <input name="name" type="text" />
      <input name="age" type="number" />
      <select name="preferred-fruits" multiple>
        <option value="apple">Apple</option>
        <option value="banana">Banana</option>
        <option value="orange">Orange</option>
        <option value="grape">Grape</option>
      </select>
      <button>Submit</button>
    </form>
  );
}
```

_Note: you can always add, change or remove data from the FormData instance after you initialise it by passing a form element as an argument._

## Using the data

We have a `FormData` instance that contains all the data you need. Now we can start using it.

### Specific value

To get and use the value of a specific key in the `FormData` instance you can use the `get` or `getAll` method.

```javascript
/**
 * `get` returns the value associated to the key that is passed as an argument.
 * 
 * If the instance has more values for the key it return the first one.
 */
const name = userFormData.get("name");

/**
 * `getAll` returns an array of all the values associated to the key that is passed as an argument.
 */
const preferredFruit = userFormData.getAll('preferred-fruits');

// ...
```

_Note: you could've seen this in action in my blog post about React's key attribute already. Read that blog post [here](https://www.nieknijland.nl/blog/why-do-we-need-to-set-a-key-prop-in-lists-in-react)._

Now you can use the value in any way you want.

If you just want to know if a specific key is present in the `FormData` instance you can use the `has` method.

```javascript
if(userFormData.has('name')) {
  // ...
} else {
  // ...
}
```

### Process all data

If you want to process the data as an iterator you can use the `entries`, `keys` and `values` method. These methods return an iterator that you can use to iterate over the `FormData` instance's data.

```javascript
const keys = userFormData.keys();
const values = userFormData.values();
const entries = userFormData.entries();

// Now loop over them using the for..of statement
for(const key in keys /** or `values` or `entries` */) {
  // ...
}
```

### Use it directly in other browser native methods

Now that you know how you can create, manipulate and use data in a `FormData` instance it time to look at applications that highlight why this instance is interesting to use. This is where it actually isolates away a lot of potentially complex code to get data from form inputs to a request that is send.

#### POST request

Let's assume that we want to send data from our form inputs to a `POST` endpoint. To realise that you have to do the following:

1. Create a form
2. Once a submit event is fired you need to initialise a FormData instance, passing the form element as an argument.
3. Manipulate the form data in any way you want.
4. Send the request

```html
<form id="user-form">
  <input name="name" type="text" />
  <input name="age" type="number" />
  <select name="preferred-fruits" multiple>
    <option value="apple">Apple</option>
    <option value="banana">Banana</option>
    <option value="orange">Orange</option>
    <option value="grape">Grape</option>
  </select>
  <button>Submit</button>
</form>

<script>
  const form = document.getElementById("user-form");
  
  form.addEventListener("submit", async () => {
    const userFormData = new FormData(form);
    
    // Edit `userFormData` in any way you like here
    
    const response = await fetch("https://your.api/api/post-endpoint", {
      method: "POST",
      // Add the `FormData` directly as the body for the request
      body: userFormData,
    });
    
    console.log(await response.json());
  });
</script>
```

Instead of worrying about trivial things, now you can focus on what matters for you. Editing the form data in any ways necessary.

_Note: if you do not want to edit the form input data in any way you do not need to use JavaScript. You can simplify things even more by using the `form` element's `method` and `action` attributes._

#### GET request

Let's do the same thing, but with a `GET` request. A real world scenario here, could be a form that functions as a filter. For example, in a webshop where you can filter on color and size.

```html
<form id="filter-form">
  <select name="color">
    <option value="red">Red</option>
    <!-- ... -->
  </select>
  <select name="size">
    <option value="s">Small</option>
    <!-- ... -->
  </select>
  <button>Submit</button>
</form>

<script>
  const form = document.getElementById("filter-form");
  
  form.addEventListener("submit", async () => {
    const filterFormData = new FormData(form);
    
    // Edit `filterFormData` in any way you like here
    
    // Pass the `FormData` instance directly into `URLSearchParams`
    const params = new URLSearchParams(filterFormData);
    
    // `params` resolves to `"color=red&size=s"`
    const response = await fetch(`https://your.api/api/get-endpoint?${params}`);
    
    console.log(await response.json());
  });
</script>
```

Again, all trivial things are isolated away and you can focus on the data.

## Conclusion

I think it's always wise to stay as close to the browser as possible. The more abstractions between your code and the browser the clunkier, heavier and more complex it gets. I hope this blog post illustrates some use-cases where the browser already has some great API's ready for you to work with.

If you liked this article and want to read more make sure to check [my other articles](https://www.nieknijland.nl/blog). Feel free to contact me on [Twitter](https://twitter.com/ngnijland) with tips, feedback or questions!