Follow me on Twitter, happy to take your suggestions on topics or improvements /Chris
50% if React developers DON'T use a form library, they build it themselves, that's HUGE number. As people discover Formik I think that number will decrease.
This article is part of series:
In this article we will cover:
This article is part of a series. Formik just has too many interesting topics that would make this article way too long. So in our next part we would cover Schema Validation with Yup, Async validation and work on making everything less verbose using some of Formiks built-in components:
## Resources I have made a repo for both these articles, so if you get stuck have a look here Form demo repo
So Forms, your favorite topic ey? No? Yea I agree with you, not my favorite either. It’s a very important topic so many things we need to get right here. Here is a non-exhaustive list:
Is this the only reason Forms are painful? Well, it kind of depends on the chosen SPA framework. In our case, we’ve chosen React.js as our SPA Framework. React currently doesn’t have an official Forms library and usually when the creator a Framework doesn’t show the way you end up with a multitude of options like:
According to a study I currently conducted on Twitter ( yes I know, not super scientific but still ) 50% of React developers opted for building their own way of handling Forms. That’s a HUGE number. My personal opinion here is to go with Formik as it covers most of the features I would expect from a Forms library. Stay with me and maybe you will agree that Formik is indeed a very capable library. 😃
Here is an article if you want to know more about how the above-mentioned libraries differ https://codebrahma.com/form-libraries-in-react/
Like all React projects we start off by using the tool Create React App, CRA. Creating a React app is as simple as typing:
npx create-react-app [myapp]
cd [my app]
Now that we have a React app lets add the library Formik to it:
yarn add formik
OR
npm install formik --save
Let’s quickly explain what we need to do to get Formik up and running. We need to do the following:
initialValues
, this will give the form the initial valuesvalidate
, this is a function that takes the form values as input parameters. The point of the function is to construct and return an object representing the state of the form. The object itself is key-value pairs where the key is the name of the form field and the value should be the error message if there is an error detected on that fieldonSubmit
, this is a function we need to define where we determine what should happen when we press submitchild
, The child of the Formik component is where we define the markup of the form and it’s containing fields. This is also where we render form errors if there are anyOk then, let’s create a file FirstExample.js, that will we will use to create a component containing Formik. Let’s start off with the import:
// FirstExample.js
import { Formik } from 'formik';
Now what? Well, we need a component that wraps the Formik component like so:
// FirstExample.js
import { Formik } from 'formik';
const FormikExample= () => (
<Formik>
// define markup
</Formik>
)
This will render nothing, but I want to take baby steps to ensure I don’t lose you on the way. Ok, so next order of business is to add a bit more markup and involved the handleSubmit method Formik component exposes so let's change your code to this:
// FirstExample.js
import React from 'react';
import { Formik } from 'formik';
const FirstExample = () => (
<Formik>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<input name="name" type="text" placeholder="Name"></input
<button>Submit</button>
</form>
)}
</Formik>
)
export default FirstExample;
If you run this in the browser at this point you will get the following error:
Yes, we need to assign a function to the onSubmit attribute of our Formik component, so let's do that next:
// FirstExample.js
import React from 'react';
import { Formik } from 'formik';
const FirstExample = () => (
<Formik onSubmit={values => {
console.log('submitting', values);
}} >
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<input name="name" type="text" placeholder="Name"></input>
<button>Submit</button>
</form>
)}
</Formik>
)
export default FirstExample;
Now let's look at the output when we hit the submit button:
It’s ok, really, we will explain WHY this happens next by talking about the life cycle of elements and hopefully bring some clarity.
Empty ey, what are we doing wrong? Ok, we need to tell the Formik component to handle the life cycle of the input elements we have in our form. We do that by defining the initialValues attribute and provide it with an object of what your form contains. We will also need to deal with the onChange event on our input element. Update your code to the following:
// FirstExample.js
import React from 'react';
import { Formik } from 'formik';
const FirstExample = () => (
<Formik
initialValues={{ name: '' }}
onSubmit={values => {
console.log('submitting', values);
}}>
{({ handleSubmit, handleChange, values }) => (
<form onSubmit={handleSubmit}>
<input onChange={handleChange}
value={values.name}
name="name"
type="text"
placeholder="Name">
</input>
<button>Submit</button>
</form>
)}
</Formik>
)
export default FirstExample;
So we did three things above
name
attributeNow, lets try pressing submit again and inspect the results:
We see now that Formik picks up our input element and handles the lifecycle properly. Oh yes 😃
So far we haven’t set up any validation, which is usually what we want do dealing with a Form. So how do we do that in our Formik component? We need to take the following steps:
Ok, let's start with the validate property:
validate = {values => {
let errors = {};
if(!values.name) {
errors.name = 'Name is required';
}
return errors;
}}
Above you see how we provide the validate property with a function that has an input parameter values
. The values parameter contains our form values and we just need to investigate those to determine whether we have an error or not. As you can see from the above implementation we are inspecting the name element and check whether it is empty. If it is empty we set an error text and lastly we return our errors object.
Next step is ensuring that our markup uses the errors object we just constructed. That’s as easy to do as adding it like so:
{({
handleSubmit,
handleChange,
values,
errors
}) => (
<form onSubmit={handleSubmit}>
<div>
<input name="name"
onChange={handleChange}
name="name"
value={values.name}
type="text"
placeholder="Name">
</input>
{errors.name &&
<span style={{ color:"red", fontWeight: "bold" }}>
{errors.name}
</span>
}
</div>
<div>
<button>Submit</button>
</div>
</form>
)}
Looking at this in a browser, it now looks like this:
There are many ways of improving how we work with Forms using Formik, two different ways are:
So far we have been showing different examples of forms where the validation is run on onChange as well as onBlur and that is the default behavior unless you explicitly shut it off. However, that has the effect of showing errors directly next to a field even though you actually haven’t even started entering characters in that field yet. That is not a great user experience. Let me illustrate the problem with a screenshot:
Above we have entered a character in the name field and erased said character so our validation function is triggered. Not only does the validation trigger when we are still in the field but the validation error is also shown for the address that we haven’t even tried interacting with. None of that is great. So what do we do? Well, we can ensure neither field shows any validation error unless they have been touched. So what does touched mean? It means we have entered characters in the field and we have left it to work on another field. Let’s show how we do that in markup:
// FormikTouched.js - excerpt showing the Formik components child function
{({
values,
errors,
touched ,
handleSubmit,
handleChange,
handleBlur
}) => (
<form onSubmit={handleSubmit}>
<h2>Form touched example</h2>
<div>
<input onBlur={handleBlur}
onChange={handleChange}
placeholder="name"
name="name"
value={values.name} />
{errors.name && touched.name &&
<div>{errors.name}</div>
}
</div>
<button>Submit</button>
</form>
)}
We see above that we add access to the touched properties as one of the properties on our input parameter for our child function. We also see that we use said touched value on our first input parameter where we access touched.name
. Essentially this means that we are able to tell that if touch.name is truthy then it’s OK to show an error. Let’s zoom in on that:
<input onBlur={handleBlur}
onChange{handleChange}
placeholder="name"
name="name"
value={values.name} />
{errors.name && touched.name &&
<div>{errors.name}</div>
}
As you can see above we need that added logic && touched.name
to ensure errors are only shown when the field has actually been interacted with.
We’ve all tried things like the above. Asked the user to be patient, to wait for the service to come back. We’ve even shown a spinner. Sooner or later we’ve come to the conclusion that we must hide or at least disable the submit button while the form is being submitted.
Formik helps us by providing a function called setSubmitting
. Let’s look at how to use it, we will need to go to our onSubmit definition:
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
As you can see above we are using setTimeout to simulate the fact that a backend call takes time and during that time we don’t want any more submits to be possible. Aren’t we missing something like disabling the submit button? Yes, we are. Here is how to do that:
<button type="submit" disabled={isSubmitting} >
Submit
</button>
When we hit our submit button the property isSubmitting
is set to true. Once we call setSubmitting(false) inside of our onSubmit
function isSubmitting
is set to false.
Ok, so we have established there are three invocations points of the validation function that we care about namely
Controlling blur behavior is done by changing the value of the attribute validateOnBlur
to false. Its default value is true, which mean it will run the validation function every time we lose focus on this element. If you know you have a costly validation such as doing async calls in your validation function it’s probably a good idea to run the validation as seldom as possible. Most forms I’ve encountered validates on blur so it’s probably a good idea to keep this functionality enabled unless validation is really really costly or you have a good reason for just running validation upon submitting the form. To control this behavior you would write the following in your markup:
<Formik validateOnBlur={false}> // to shut it off
As for change events, those are triggered every time you change a character, now usually that’s just way too often in my opinion but you might have valid reasons for using this one. To control its behavior type:
<Formik validateOnChange={false}> // to shut it off
We started talking about Forms, different ways of doing validation, when to validate, how much to put in a form and so on. Continuing we mentioned different Form libraries besides Formik. Thereafter we continued with Formik in particular and looked at how to install and set it up and also step by step build out our Form. Lastly, we looked at different ways of improving our Form.
However, there is much more to this library that’s worth mentioning so we’ve saved certain parts such async validation, Schema validation with Yup and using Formiks built-in components for an even more painless experience with forms.
This was a bit of a lengthy post but there were some GIFs in there so hopefully, you’ve made it all the way to here. In the next post we will learn how to use Formik event better and more efficiently, so stay tuned.