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 atype
attribute set to'submit'
- define an
onSubmit
handler under theform
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:
call the
preventDefault
method of the HTMLFormEvent
type, passed as an argument into our function by the HTML Goddess herself (we named ite
). 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)Pass the form element (referenced as
e.target
) in aFormData
constructor & store it in ourformData
constant.
This will take the input elements and parse them into key-value pairs where the key is what we defined in thename
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!
Top comments (60)
That's it!
It was really hard on my last projects to make junior devs to understand that, because if you only learnt react that's what you'll find in the doc about managed/controlled forms so React is the only "source of truth".
That's useful on multi-step forms but I still can't find a way in which this is better than using the DOM itself through HTML.
Moreover you can set validations as well in the HTML using the pattern attribute and use
:valid
and:invalid
CSS selectors to style them depending on the state they're in (I thought I made a post on that stuff but I just realized I didn't so maybe I should cover all that stuff in a single place ๐).Yeah, that appears quite a large bite for a junior dev to gulp ๐
Thanks for the comment btw โจ
Yes but just because some years ago people learnt HTML to the detail, then CSS, then JS browser API and then frameworks/libs.
I'm finding lately a good amount of juniors that don't know what is the real scope of React... I've even told by one that
map
is a React method ๐ so my guess is that the issue is the learning path itself...This is a legit issue, and new frameworks like svelte for example, make you learn more of "svelte" and not more of "javascript". Svelte is cool & I love it, but I think one should know fundamentals of Vanilla JS before dipping toes in libraries/frameworks
Totally agree!
Thanks for bringing this up! Just because youโre using JavaScript to render the elements doesnโt mean you shouldnโt also render markup that does its own work tooโฆ use the tools for what theyโre made for!
Please do write a post and share all the knowledge you have.
I do it from time to time, check my profile!
Also follow to receive new posts in your feed ๐
Hi @anubha143 I finally bring this up, check it out! ๐
Form input validation WITHOUT JavaScript
JoelBonetR ใป Aug 15 ใป 2 min read
Nice way of handling forms. We have to highlight the limitations as well though. You still need
value
andonChange
when you want an input to update when another one is changed. Also if the form rerenders because of another component, your inputs will be cleared (happened to me with a component that shows the current time.) But for simple forms, where it's guaranteed that there will be no rerenders while you fill the form, it can save time.Form data is very nice yes :D For the cases where you need to do validation while the user is typing, or otherwise need to have controlled inputs, you can use something like Formik, or put state in a reducer:
@brense Input validation on the spot without even using JS at all:
Form input validation WITHOUT JavaScript
JoelBonetR ๐ฅ ใป Aug 15 '22 ใป 2 min read
<input type="email" />
as example)Hmm, might give that a try... Have used the pattern prop before to force number keypad on phone etc, but never really considered it for actual validation... I do still think there's a use-case for linking actual state to the inputs when you want to do some calculated state for example, but for validation this looks pretty nice at first glance.
Thanks for sharing, cool article. Especially the
new FormData(e.target)
part make things more easy.My opinion: don't use "controlled components". If you have them, remove them and use the approach
<form onSubmit={..}>
. If you need validation, you can check input inonChange
and use likesetUsernameError(..)
if you need to really render the component to show an error, but not every keyboard hit will make it render.At least there are some libs for forms.
Thanks for the comment, Philipp :)
Yes, you're right, and I personally use the same approach too, but I have seen in codebases, sometimes people like to use this
value{ x } onChange={e => setX(e.target.value)}
Thought might include it as well!
Very cool, a much better way to interact with forms and save on that precious memory.
If you're going to be using this method a few times it's worth having a little hook with common functions built in, rather than writing them over and over.
Thanks for the code,
Great idea to abstract the logic into a hook ๐๐ป
What about handling forms like this?
I've been using this method for some time now, without any issues
Very cool article! I did the same mistake in my early stages of react, generating a state for every property which I needed in my form :). Can you enlighten me one more. I used react-hook-form in the past. For controlled inputs I think it's unbeatable but would you still use the native html approach?
Man do what suits you best, Personally for me, I like to minimise package dependencies, thus would use the native HTML approach until it takes a lot of work to roll with it, then I would look for other alternatives as needed :)
Sounds legit. Not depending on a lib is always good. Can you do also dependant validation? Lets say if a=dog, b can only be "good Boy | bad Boy" ? Otherwise it has no validation.. smth like that? Is that possible with native HTML validators? I'm not experienced with that sorry if that sounds kinda stupid
Dont be sorry for asking stupid questions, NEVER.
Coming to HTML-native way of validating, HTML can check for empty fields but complex validation? no, you have to implement that yourself in the
onSubmit
handler.See this code : codesandbox.io/s/trusting-torvalds...
Try submitting the form without entering any value ;)
Thanks. Yeah I was aware of "simple" validation. Ok if I need something more complex I will continue using react-hook-form :)
This is quick and neat for very small or pet projects, but please use formik or react-hook-form for any real client production apps
Can you justify somehow this statement? There are better options
Not sure you've used this, but you can control them all in a single state with something like this:
Is it safe to say that generating multiple useStates for a form can cause a performance issue? Also I wonder why if there are better ways to handle input why donโt they teach that in bootcamps? Thatโs frustrating