Photo by Liane Metzler

What are forms? Why are they used? How can a form be useful for the user and the recipient? In real life, you fill forms for many different uses: to submit a specific request, to place an order, to provide required information, and so on. In essence, a form is a way to provide structured and labeled data as opposed to unstructured and unlabelled data.

Forms allow users to interact with the database

A form is efficient because the data received from a form has a predefined and predictable structure. This enables parsing and automatic treatment of the submitted data. The information is structured into named properties. The data still needs to be validated, client-side and/or server-side. The data is ready for being pushed into a database.

Websites and apps use forms. A contact form requires the user to provide his name, his email and the subject of the email. In a sense, it requires the user to provide more data than usual. In other situations, form can require the user to provide less data than usual, by focusing only on the most important piece of information, while denying any additional or superfluous information.

Forms in PWA

In a Progressive Web App, the whole application is loaded once and for all on the first page load. The server won't have work to do anymore, as the application can handle all the events and the network calls by itself. As such, a Progressive Web App doesn't need and doesn't want any page reload or page change, as a page reload would lead to the App restart.

By default, submitting a HTML form triggers a HTTP request, providing the submitted data to the server. The browser then loads a new page provided by the server. This behaviour was designed when all the logic was handled by the server.

In a PWA, the form default behaviour is blocked. The app might still upload the data to a server, but this upload is made on the background and asynchronously. As such, the upload is non-blocking and doesn't trigger unnecessary reload.

Forms in React

In React, the form inputs are either controlled or uncontrolled.

Controlled inputs have their value dictated by the state. The input mutates the state in order to mutate the value. As a side-effect, the component renders for each keystroke as this leads to a change in the state.

import React from "react"; export default function App() { function handler(event) { event.preventDefault(); console.log({ firstName, lastName }); } const [firstName, setFirstName] = React.useState(""); const [lastName, setlastName] = React.useState(""); return ( <form onSubmit={handler}> <input type="text" onChange={e => setFirstName(} value={firstName} /> <input type="text" onChange={e => setlastName(} value={lastName} /> <input type="submit" /> </form> ); }

Uncontrolled inputs are standard DOM elements and their value are not stored in the state. Their values needs to be retrieved imperatively through their assigned reference. Both solutions are not ideal and involve some boilerplate code.

import React from "react"; export default function App() { function handler(event) { event.preventDefault(); const firstName = firstNameRef.current.value; const lastName = lastNameRef.current.value; console.log({ firstName, lastName }); } const firstNameRef = React.useRef(null); const lastNameRef = React.useRef(null); return ( <form onSubmit={handler}> <input type="text" ref={firstNameRef} /> <input type="text" ref={lastNameRef} /> <input type="submit" /> </form> ); }

Using React-Hook-Form reduces boilerplate while also removing unnecessary renders by using uncontrolled components. It also prevents the form submission by default.

import React from "react"; import { useForm } from "react-hook-form"; export default function App() { function handler(data) { console.log(data); } const { register, handleSubmit } = useForm(); return ( <form onSubmit={handleSubmit(handler)}> <input name="firstName" type="text" ref={register} /> <input type="text" name="lastName" ref={register} /> <input type="submit" /> </form> ); }

Text inputs vs. Specific inputs

Text inputs are straightforward and receive unformatted text values.

Date inputs expect correctly formatted date values. Date values are a pain to collect because date formats are different from one country to another, and because browsers do not provide a good interface to collect dates. The most reliable way to collect a date is to collect each component in a separate input: year, month and day.

Select inputs provide a list of possible choices. They offer discoverability of the different choices. While the interface is not ideal when the list is very big, the user can speed-up the process by typing the first letter of what he's looking for instead of scrolling.