I was creating Forms the wrong way all along in React.js ๐Ÿค”

I was creating Forms the wrong way all along in React.js ๐Ÿค”

ยท

4 min read

Play this article

Introduction

When I was creating a signup form, I found myself creating dozens of useStates & then creating dozens of onChange handlers for those useStates. Something like this ๐Ÿคฎ

Man I feel sleepy even writing this for illustration!

Also, you're welcome, for blasting your eyes with that monstrous One Light theme code snippet. Some amount of white is good for attention aye! ๐Ÿ˜‰

So... you get the point, In this post, I'll be trying to solve this problem in an elegant manner (certainly not the BS I did in my previous post, making a buggy React Form component which no one even bothers to have a look at!)

Let's Get Started!

Code

export default function App() {

  // NOT a even a SINGLE useState babyyyyyyy! ๐Ÿ˜Ž

  const submitForm = (e) => {
    e.preventDefault();

    const formData = new FormData(e.target);
    const inputObject = Object.fromEntries(formData); // convert the FormData object to a JSON object

    console.log(inputObject);
  };

  return (
    <div className="App">
      <form onSubmit={submitForm}>
        <div>
          <input name="email" placeholder="email" />
        </div>

        <div>
          <input name="password" placeholder="password" />
        </div>

        <div>
          <input name="phone" placeholder="phone" />
        </div>

        <br />

        <button type="submit">Submit</button>
      </form>
    </div>
  );
}

You can try this code & tinker with it in the same browser as you're viewing this post, thanks to codesandbox (code link)

For my beginner friends, who are new to React, what we did here was:

  • wrap the input fields in an actual HTML form element
  • define the name attribute of each of the input fields (can be anything, HTML uses this attribute to name the input value against it)
  • create a button with no onClick handler but a type attribute set to 'submit'
  • define an onSubmit handler under the form element

After the user has done typing their details in the input, clicking on the button with type='submit' declared in the form, will cause the HTML form element to call its onSubmit handler i.e our submitForm function.

const submitForm = (e) => {
  // 1
  e.preventDefault();

  // 2
  const formData = new FormData(e.target);

  // 3
  const inputObject = Object.fromEntries(formData); // convert the FormData object to a JSON object
  console.log(inputObject);
};

Now, we've done 3 things here:

  1. call the preventDefault method of the HTML FormEvent type, passed as an argument into our function by the HTML Goddess herself (we named it e). This function prevents the form from continuing its default behaviour after submission which includes automatically making a GET request to the same page's URL with the form input values as payload AND reloading the page (we don't want that because we're defining our own form submit logic whose functioning will be interrupted by a page reload)

  2. Pass the form element (referenced as e.target) in a FormData constructor & store it in our formData constant. This will take the input elements and parse them into key-value pairs where the key is what we defined in the name attribute against our inputs & the value will be their corresponding input text. All the different input text values can be retrieved using their name, something like this:

    // quite similar to the map syntax to get a key ( map.get("key") )
    const email = formData.get("email")
    

    Isn't that quite magical AND elegant? HTML does all the work for you from parsing the form input values to collecting the data & returning it in a map-like structure ๐Ÿช„

  3. Last but not least, we convert the FormData object which has our input values, to a plain JavaScript object with Object.fromEntries( ... ). Logging the, now created object, gives this output:

IT WORKS!

BUT! and that's a big but (pun intended), the cons of this approach is that you CAN'T write Controlled Inputs. For that, you HAVE TO declare a useState & It's corresponding onChange handler
"What the hell is a controlled Input?"
Have a look at this example

This is a controlled input in React js, but for inputs like this, we can use the hybrid approach:

  • Define all the inputs in a form
  • Write useState & onChange handler ONLY for those inputs that are controlled
  • Then, manually set values in the formData to those controlled state variables

P.S:
Seeing HTML automatically handling inputs for us appears to be some kind of magic and to be honest, I hate when magic is occurring BUT some amount of magic is bearable for the sake of making the developer experience good ๐Ÿ‘Œ๐Ÿป


Aaaand... that's a wrap! Like this post, if you liked it ๐Ÿ™ƒ
But if you loved it? man you gotta follow me on Twitter ๐Ÿ˜‰

Bye for now!


ย