Building complex forms is a common part of software development for both mobile and the web. Many of the apps we’ve worked on at Tacchi have made use of them in one way or another, for example account and profile creation flows, e-commerce checkouts, and posting job advertisements. Forms written in React, however, a powerful JavaScript library for building user interfaces, can be very verbose, cutting into development time that could be spent on other aspects of the app and potentially impairing maintainability.
In this article I’ll be talking about mitigating these challenges through use of the Formik library. With Formik we’ll decrease boilerplate code and increase development speed, leading to happier clients and happier developers. Note that while the example I’ll be working with below is for a web form, Formik can also be used with mobile apps written in React Native.
Forms written in plain React
One of the first things that struck me when I started using React was the amount of boilerplate code required. This was especially true when building forms, which seemed overly verbose and redundant. For example, consider the following form, which I used for a Formik-related knowledge-sharing session we held at Tacchi:
We’re setting the form’s initial values in the first usage of useState
. We’re also using state to store error messages and to keep track of whether the form is being submitted. We then have several event handlers to update said values and errors whenever an input changes or a user clicks on an input field and then navigates away from it. Additionally, we have an event handler for form submission, which changes the form’s submitted values into text strings and displays them in an alert box while also preventing the form from being mistakenly double-submitted.
Now while the above is a perfectly usable form, having to write all of the event handlers and validation logic can take a substantial amount of time, particularly for more involved forms. Having less code would be easier to maintain and would speed up development—wins for both Tacchi and our clients. What if I told you there’s a way we could do just that, and without affecting functionality?
Enter Formik
While there are a number of libraries for taking the redundancy out of form building with React, far and away the favorite, in terms of downloads at least, is Formik. As it states in the GitHub repo, Formik helps with handling form state in regard to values, validations, and submission-handling. You make use of Formik by nesting your form within the provided Formik component and then exposing your values, errors, and event handlers through that component’s props.
Additionally, Formik works nicely with various validators, notably Yup, simplifying another part of the process. By using Formik and Yup instead of your own custom event handlers and validations, you can slash the number of lines of code required to get your form working.
On to the meat of this article: Let’s try refactoring the above example code to see how Formik can help simplify it.
First, we nest our form inside a Formik component:
The Formik component has an initialValues
prop which we can use instead of defining those values in useState
:
We’ve now delegated part of our state management to Formik. Next, let’s get rid of our custom handleChange
and handleSubmit
event handlers and use Formik’s event handlers instead:
If you’re testing this in a browser, you’ll now notice the Submit button no longer works. Checking the console, we see the following error:
Uncaught (in promise) TypeError: _this.props.onSubmit is not a function.
Formik lets us define our own onSubmit
prop. This will be called by Formik’s handleSubmit
event handler. Let’s make use of that prop to replicate the original functionality; i.e., displaying the form values in an alert box as a stringified JavaScript object:
Our form is now working again.
Next, let’s tackle validation. Since I’m using the onBlur
event attribute to fire our validations, let’s first replace our handleBlur
event handler with that of Formik’s:
This will give us a prop for each named input that we can use to determine when a user has deselected an input (“touched”) so that we can display an error message if a user tabs away from an input without first entering valid data (for example, an incorrectly formatted email address). Before we get to that, however, as all our custom validations were removed when we erased the handleBlur
event handler, we can replace and simplify these by using a validator library with Formik.
Validating with Yup
Yup is so well loved by Formik’s creator he’s created a special prop to ease its implementation, validationSchema
. We can add Yup by defining a Yup schema object with validation errors for our inputs and passing it to the aforementioned prop. (While there are a great number of options available when defining a Yup schema object, for simplicity’s sake I’ve only replicated validations similar to those of our original form.)
We need to check that each input has been touched before we display error messages as otherwise error messages will appear while we are in the middle of inputting a value and tabbing away from one input field will mistakenly display errors for both inputs:
Since we’re now using the Formik component to keep track of our errors, we can remove all references to useState
entirely, provided we update onSubmit
to also set isSubmitting
:
Lastly, let’s make use of Formik’s helper components to further dry up our code. We’re going to be using the following helpers:
Field
, which receives the appropriatehandleChange
andhandeBlur
event handlers based on the input’s name attribute.ErrorMessage
, which checks both whether the input has been touched and whether it has any errors.Form
, which receivesonSubmit
.
And with that, we’ve now reached the end of our little refactoring exercise. We’ve taken a file that was a bit over 100 lines and cut it down to one that’s only about 60—nearly half its original size!—and we’ve done so without sacrificing readability. In fact, it’s even easier to read now, being both shorter and simpler, and appears visually identical. By keeping our code as simple as possible (but no simpler!), we ensure a higher degree of maintainability with increased development speed.
While the above example is rather simplistic, I hope it’s given you a feel for the advantages Formik can provide. It’s not the right solution for every form-related problem that comes from using React, but it can help lessen much of the pain. If you haven’t already, give Formik a try in your next project.