DEV Community

ngnijland
ngnijland

Posted on • Originally published at nieknijland.nl

Using FormData to process form input values

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.

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);
});
Enter fullscreen mode Exit fullscreen mode

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.

<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>
Enter fullscreen mode Exit fullscreen mode

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

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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.

/**
 * `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');

// ...
Enter fullscreen mode Exit fullscreen mode

Note: you could've seen this in action in my blog post about React's key attribute already. Read that blog post here.

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.

if(userFormData.has('name')) {
  // ...
} else {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

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.

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` */) {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

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
<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>
Enter fullscreen mode Exit fullscreen mode

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.

<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>
Enter fullscreen mode Exit fullscreen mode

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. Feel free to contact me on Twitter with tips, feedback or questions!

Top comments (0)