{"id":1969,"date":"2020-12-03T11:01:17","date_gmt":"2020-12-03T05:31:17","guid":{"rendered":"http:\/\/uitutorials.in\/wp\/?p=1969"},"modified":"2020-12-03T11:58:32","modified_gmt":"2020-12-03T06:28:32","slug":"how-to-handle-forms-with-just-react-react-form-i","status":"publish","type":"post","link":"https:\/\/uitutorials.in\/wp\/how-to-handle-forms-with-just-react-react-form-i\/","title":{"rendered":"How to handle forms with just React &#8211; React Form I"},"content":{"rendered":"<p>First thing I wish to mention is how powerful and underestimated DOM API is. It has a somewhat bad reputation, though, because it\u2019s completely imperative. That\u2019s why libraries like react, vue, angular, etc exist in the first place: to abstract away the imperative nature of DOM api. I think this creates a wrong impression with the beginners who jump to the conclusion that you should stay away from it. You should indeed avoid imperative code, but you should also embrace all the power that the browser and the DOM give you.<\/p>\n<h3>How to create forms with react and send data to the server?<\/h3>\n<p><strong>The most minimalistic approach<\/strong><\/p>\n<pre class=\"line-numbers\"><code class=\"language-javascript\">class MyForm extends React.Component {\r\n  constructor() {\r\n    super();\r\n    this.handleSubmit = this.handleSubmit.bind(this);\r\n  }\r\n\r\n  handleSubmit(event) {\r\n    event.preventDefault();\r\n    const data = new FormData(event.target);\r\n    \r\n    fetch('\/api\/form-submit-url', {\r\n      method: 'POST',\r\n      body: data,\r\n    });\r\n  }\r\n\r\n  render() {\r\n    return (\r\n     &lt;form onSubmit={this.handleSubmit}&gt;\r\n        &lt;label htmlFor=\"username\"&gt;Enter username&lt;\/label&gt;\r\n        &lt;input id=\"username\" name=\"username\" type=\"text\" \/&gt;\r\n\r\n        &lt;label htmlFor=\"email\"&gt;Enter your email&lt;\/label&gt;\r\n        &lt;input id=\"email\" name=\"email\" type=\"email\" \/&gt;\r\n\r\n        &lt;label htmlFor=\"birthdate\"&gt;Enter your birth date&lt;\/label&gt;\r\n        &lt;input id=\"birthdate\" name=\"birthdate\" type=\"text\" \/&gt;\r\n\r\n        &lt;button&gt;Send data!&lt;\/button&gt;\r\n      &lt;\/form&gt;\r\n    );\r\n  }\r\n}<\/code><\/pre>\n<p>Ok, where are the value attributes or the onChange callbacks? Well, you\u2019re not required to use them. The onSubmit callback gets called when you submit the html form by either clicking on the submit button or just by pressing \u201center\u201d while focused in one of the input fields. <strong>When you add name attributes to your inputs, you add structure to your form.<\/strong> This structure can be serialized by the native FormData interface (basic support in all browsers and IE10+). All you do is pass in a form element (which we access via event.target) to the FormData constructor and you get a serialized interpretation of the inputs which can be sent to the server.<br \/>\nAlso notice that we don\u2019t add an onClick listener to the button. If we did, we would not be able to respond to submit events triggered from the keyboard (by pressing enter). That\u2019s bad UX. By using the onSubmit callback we cover both cases.<br \/>\nBy using this method, no matter how large your form grows, you don\u2019t need to write any additional boilerplate code. Just follow a good practice of always adding a name attribute to your input tags (of course these names should correspond to what the server expects).<br \/>\nAlso notice how we have kept our form component fully declarative even without using such react features as \u201ccontrolled components\u201d. No refs or raw DOM manipulations are needed. Here\u2019s the link to the fiddle with the form.<\/p>\n<h3>My data requires to be transformed from user input, so I need state and complex controlled components!<\/h3>\n<p>Well, no, not necessarily.<br \/>\nOne of the first things you learn when starting with react is that data should have a single source of truth and that it should flow one way, top to bottom. That\u2019s true. But where is the \u201csource\u201d of the form data? It depends on the kind of application you have.<br \/>\nIt\u2019s very likely that the data for the form comes from the user input and from nowhere else, and it is not shared anywhere else.<br \/>\nOne of valuable lessons you learn from react docs is that when you have to share state, you should lift the state up. But be careful: <strong>don\u2019t lift the state when you don\u2019t need to.<\/strong><br \/>\nYes, there are cases where controlled inputs are a valid choice. I plan to cover those cases in my next post.<br \/>\nBut for now I would like to explore how far you can go with the simple approach described above without reaching out for controlled inputs.<\/p>\n<h3>Input data transformations<\/h3>\n<p>Imagine the data that the server needs is in different form than the data that the user enters. Let\u2019s say we have these requirements:<br \/>\n1.the user enters the date in MM\/DD\/YYYY format, but the server expects it in YYYY-MM-DD<br \/>\n2.the username should be sent in uppercase<\/p>\n<pre class=\"line-numbers\"><code class=\"language-javascript\">handleSubmit(event) {\r\n    event.preventDefault();\r\n    const data = new FormData(event.target);\r\n    \/\/ NOTE: you access FormData fields with `data.get(fieldName)`    \r\n    const [month, day, year] = data.get('birthdate').split('\/');\r\n    const serverDate = `${year}-${month}-${day}`;\r\n    data.set('birthdate', serverDate);\r\n    data.set('username', data.get('username').toUpperCase());\r\n    fetch('\/api\/form-submit-url', {\r\n      method: 'POST',\r\n      body: data,\r\n    });\r\n}<\/code><\/pre>\n<p>That was very easy. The best thing about it is that you didn\u2019t have to search for a framework- or plugin-specific way to do this. Remember $parsers and $formatters pipeline? It wasn\u2019t a bad feature, it was a great feature. I enjoyed using it. But I think you\u2019ll agree that it\u2019s an overkill for this particular case.<br \/>\nThere\u2019s one downside, though. Did you notice how we have tied ourselves to particular pieces of input? Inside the handleSubmit handler we now have to know which inputs need to be transformed and which don\u2019t. We\u2019ve lost some declarativity. Let\u2019s solve this problem.<\/p>\n<p><i>from now on when the user-entered value needs to be transformed I\u2019ll call that \u201cparsing\u201d.<\/i><br \/>\nDepending on your app you might need different parser functions for different kinds of inputs. But across your app you\u2019re probably going to reuse many of them. Reusing code is something we do all the time. Why not create a set of utility functions that are responsible for parsing your form inputs? Here\u2019s an example:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-javascript\">const inputParsers = {\r\n  date(input) {\r\n    const [month, day, year] = input.split('\/');\r\n    return `${year}-${month}-${day}`;\r\n  },\r\n  uppercase(input) {\r\n    return input.toUpperCase();\r\n  },\r\n  number(input) {\r\n    return parseFloat(input);\r\n  },\r\n};<\/code><\/pre>\n<p>Just a javascript object with methods. But how do we use it? Well\u2026<\/p>\n<h3>Time to find out more about DOM api<\/h3>\n<p>Any &lt;form \/&gt; element has an elements property. Read more about it here. It\u2019s an html collection object. The best thing about it is it provides access to all input nodes of the form by key, where the key is the input\u2019s name attribute. Yes, no matter how deep inside the form you have an &lt;input name=&#8217;birthdate&#8217; \/&gt; element, you can access it with form.elements.birthdate. Isn\u2019t that great?<\/p>\n<p>Ok, we can access all form\u2019s input nodes by their name. But how do we know which ones to parse? How can we mark those inputs that do need some additional parsing?<br \/>\nThe data-attributes of course! That\u2019s what they\u2019re here for. So to indicate that an input\u2019s value needs to be transformed to uppercase before we send it to the server I suggest adding a data-parse attribute to it:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-javascript\">&gt;input\r\n  name=\"username\"\r\n  type=\"text\r\n  data-parse=\"uppercase\"\r\n\/&lt;\r\n<\/code><\/pre>\n<p>Very descriptive. And here\u2019s the whole example showing how we can transform all necessary data. Pay attention to the \u201chandleSubmit\u201d handler:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-javascript\">import React from 'react';\r\n\r\nconst inputParsers = {\r\n  date(input) {\r\n    const [month, day, year] = input.split('\/');\r\n    return `${year}-${month}-${day}`;\r\n  },\r\n  uppercase(input) {\r\n    return input.toUpperCase();\r\n  },\r\n  number(input) {\r\n    return parseFloat(input);\r\n  },\r\n};\r\n\r\nclass MyForm extends React.Component {\r\n  constructor() {\r\n    super();\r\n    this.handleSubmit = this.handleSubmit.bind(this);\r\n  }\r\n\r\n  handleSubmit(event) {\r\n    event.preventDefault();\r\n    const form = event.target;\r\n    const data = new FormData(form);\r\n\r\n    for (let name of data.keys()) {\r\n      const input = form.elements[name];\r\n      const parserName = input.dataset.parse;\r\n\r\n      if (parserName) {\r\n        const parser = inputParsers[parserName];\r\n        const parsedValue = parser(data.get(name));\r\n        data.set(name, parsedValue);\r\n      }\r\n    }\r\n    \r\n    fetch('\/api\/form-submit-url', {\r\n      method: 'POST',\r\n      body: data,\r\n    });\r\n  }\r\n\r\n  render() {\r\n    return (\r\n      &lt;form onSubmit={this.handleSubmit}&gt;\r\n        &lt;input\r\n          name=\"username\"\r\n          type=\"text\"\r\n          data-parse=\"uppercase\"\r\n        \/&gt;\r\n\r\n        &lt;input name=\"email\" type=\"email\" \/&gt;\r\n\r\n        &lt;input\r\n          name=\"birthdate\"\r\n          type=\"text\"\r\n          data-parse=\"date\"\r\n        \/&gt;\r\n\r\n        &lt;button&gt;Send data!&lt;\/button&gt;\r\n      &lt;\/form&gt;\r\n    );\r\n  }\r\n}\r\n\r\nexport default MyForm;<\/code><\/pre>\n<p>That\u2019s pretty powerful, in my opinion. Once again, our forms can grow as large as they need without making our handler function bigger.<br \/>\nAnd the best thing is that not only we did not tie ourselves to any react-form-handling library, we hardly tied ourselves to react. If one day you decide to abandon react for any reason, you don\u2019t have to noticeably change the way you deal with forms. The DOM is not going anywhere.<\/p>\n<h3>Input validation<\/h3>\n<p>If you\u2019re observant, you may have noticed another problem with the above form. We don\u2019t check inputs for validity before parsing them, which may lead to errors. If you think that DOM api can\u2019t help us here or that it\u2019s not supported well, I\u2019m happy to say that you\u2019re wrong. Html form validation is another powerful thing I like using very much.<br \/>\nThe most simple example:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-javascript\">&lt;form&gt;\r\n  &lt;input name=\"username\" type=\"text\" required \/&gt;\r\n&lt;\/form&gt;<\/code><\/pre>\n<p>That\u2019s it. We only added a required attribute to an input. The browser will consider this input field invalid if it is empty and valid if it has at least one character. When at least on of the the input fields is invalid, the browser will not let the user submit the form, instead it will show a tooltip near the first invalid input.<\/p>\n<p>But the thing is, we cannot rely on this behavior: the browser will not prevent the form from being submitted in Safari and mobile safari. But we don\u2019t need to! The browser tooltips are not the best way to go anyway: they are not flexible enough and are not easily styleable.<\/p>\n<p>What we do is this:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-javascript\">&lt;form noValidate&gt;\r\n  &lt;input name=\"username\" type=\"text\" required \/&gt;\r\n&lt;\/form&gt;<\/code><\/pre>\n<p>We add a novalidate attribute (noValidate in jsx turns into novalidate in html). The name of the attribute is somewhat misleading. When we add it, <strong>we do not actually turn off form validation.<\/strong> We only prevent the browser from interfering when an invalid form is submitted so that we can \u201cinterfere\u201d ourselves.<br \/>\nSo, how does form validation work now? Like this: a &lt;form \/&gt; element has a checkValidity() method which returns false when the form is considered invalid and true when it is valid. A form is considered invalid when at least one of its input elements is invalid. Here\u2019s a small demo-gif:<\/p>\n<p>And here\u2019s how we can use this in our handleSubmit method:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-javascript\">handleSubmit(event) {\r\n  if (!event.target.checkValidity()) {\r\n    \/\/ form is invalid! so we do nothing\r\n    return;\r\n  }\r\n  \/\/ form is valid! We can parse and submit data\r\n}<\/code><\/pre>\n<p>Great! Now we have a way to prevent parsing invalid inputs when the form is not valid.<br \/>\nCan we detect individual invalid inputs? Sure! How? Exactly the same: inputs also have a .checkValidity() method. See for yourself:<br \/>\nAdding a required attribute is not the only way to tell the browser that an input needs to be checked. What else can we do?<br \/>\n1. Use the pattern attribute. The most powerful validation attribute. Its value should be a regex which will be matched against the whole input value.<br \/>\nLet\u2019s say we want to allow only numbers in some input field. Here\u2019s how we can do it: &lt;input type=&#8221;text&#8221; pattern=&#8221;\\d+&#8221; \/&gt;. That\u2019s it. We can now call .checkValidity() method of the input and check whether it is valid or not.<br \/>\n2.Give an input a type email. Just like the name suggests, the input will be checked for a valid email pattern, so we don\u2019t have to come up with a quirky regex solution.<\/p>\n<p>Ok, let\u2019s use this knowledge. Here\u2019s what our form code may look like now:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-javascript\">&lt;form onSubmit={this.handleSubmit} noValidate&gt;\r\n  &lt;label htmlFor=\"username\"&gt;Username:&lt;\/label&gt;\r\n  &lt;input\r\n    id=\"username\"\r\n    name=\"username\"\r\n    type=\"text\"\r\n    data-parse=\"uppercase\"\r\n  \/&gt;\r\n  &lt;label htmlFor=\"email\"&gt;Email:&lt;\/label&gt;\r\n  &lt;input id=\"email\" name=\"email\" type=\"email\" \/&gt;\r\n  &lt;label htmlFor=\"birthdate\"&gt;Birthdate:&lt;\/label&gt;\r\n  &lt;input\r\n    id=\"birthdate\"\r\n    name=\"birthdate\"\r\n    type=\"text\"\r\n    data-parse=\"date\"\r\n    placeholder=\"MM\/DD\/\/YYYY\"\r\n    pattern=\"\\d{2}\\\/\\d{2}\/\\d{4}\"\r\n    required\r\n  \/&gt;\r\n  &lt;button&gt;Send data!&lt;\/button&gt;\r\n&lt;\/form&gt;<\/code><\/pre>\n<p>Here is a complete fiddle with validation and here\u2019s a glimpse of how it can work:<\/p>\n<h3>Visualizing invalid inputs<\/h3>\n<p>I know what you might be thinking. If the input\u2019s validity can be checked with a .checkValidity() method, then we can toggle validity classes with javascript!<br \/>\nWrong! Well, that\u2019s not really \u201cwrong\u201d, but I have a much better solution to offer. Let\u2019s find out one more powerful thing about form API.<\/p>\n<p><strong>The :invalid css selector<\/strong><br \/>\nThose inputs which are invalid can be targeted with pure css! Just like this:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-javascript\">input:invalid {\r\n  border-color: red;\r\n}<\/code><\/pre>\n<p>Again, I know what you might be thinking. \u201cSuch css magic probably has terrible browser support\u201d. I\u2019m so happy to correct you: it has wonderful browser support!<br \/>\nThere are some drawbacks, though. While this css pseudo-selector is powerful, it\u2019s also pretty dumb. Let\u2019s say we want to style invalid inputs only after the user has tried to submit the form. The form has no notion of being \u201cdirty\u201d or having a \u201ctried-to-submit\u201d state. So the invalid inputs will be marked with a red border even before the user has tried typing anything at all.<br \/>\nHow can we deal with this? Very easily! We can just add a \u201cdisplayErrors\u201d class to the form itself after the user tries to submit it and style the inputs as invalid only when they are inside a form with a .displayErrors class:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-javascript\">handleSubmit(event) {\r\n  if (!event.target.checkValidity()) {\r\n    this.setState({ displayErrors: true });\r\n    return;\r\n  }\r\n  this.setState({ displayErrors: false });\r\n}\r\nrender() {\r\n  const { displayErrors } = this.state;\r\n  return (\r\n    &lt;form\r\n      onSubmit={this.handleSubmit}\r\n      noValidate\r\n      className={displayErrors ? 'displayErrors' : ''}\r\n    &gt;\r\n      {\/* ... *\/}\r\n    &lt;\/form&gt;\r\n  );\r\n}<\/code><\/pre>\n<p>\u2026 and in our css:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-javascript\">.displayErrors input:invalid {\r\n  border-color: red;\r\n}<\/code><\/pre>\n<p>I hope I have convinced you that native DOM API is a powerful thing that doesn\u2019t get enough attention and that you should use it. It\u2019s flexible enough to give you whatever behavior you desire.<br \/>\nWhat about \u201ccontrolled\u201d inputs, though? There are lots of cases when you need them. In this post I wanted to get you familiar with the forms API and explore its power. Not using controlled inputs serves this purpose very well.<br \/>\nYou might think that when you have \u201ccontrolled\u201d inputs you do not need this DOM API. But I assure you that these things are complementary. In my next post I want to explore how using native forms api together with controlled input can make you even more powerful. But I also want to emphasize that you shouldn\u2019t seek a more powerful tool when you can easily do without it.<\/p>\n<h3>Reference<\/h3>\n<p><a href=\"https:\/\/medium.com\/@everdimension\/how-to-handle-forms-with-just-react-ac066c48bd4f\" rel=\"noopener\" target=\"_blank\">how-to-handle-forms-with-just-react<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>First thing I wish to mention is how powerful and underestimated DOM API is. It has a somewhat bad reputation, though, because it\u2019s completely imperative. That\u2019s why libraries like react, vue, angular, etc exist in the first place: to abstract away the imperative nature of DOM api. I think this<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[106],"tags":[107,129],"class_list":["post-1969","post","type-post","status-publish","format-standard","hentry","category-react","tag-react","tag-react-form","ct-col-2"],"_links":{"self":[{"href":"https:\/\/uitutorials.in\/wp\/wp-json\/wp\/v2\/posts\/1969","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/uitutorials.in\/wp\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/uitutorials.in\/wp\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/uitutorials.in\/wp\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/uitutorials.in\/wp\/wp-json\/wp\/v2\/comments?post=1969"}],"version-history":[{"count":5,"href":"https:\/\/uitutorials.in\/wp\/wp-json\/wp\/v2\/posts\/1969\/revisions"}],"predecessor-version":[{"id":1981,"href":"https:\/\/uitutorials.in\/wp\/wp-json\/wp\/v2\/posts\/1969\/revisions\/1981"}],"wp:attachment":[{"href":"https:\/\/uitutorials.in\/wp\/wp-json\/wp\/v2\/media?parent=1969"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/uitutorials.in\/wp\/wp-json\/wp\/v2\/categories?post=1969"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/uitutorials.in\/wp\/wp-json\/wp\/v2\/tags?post=1969"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}