By Richard O
One of our applications makes use of several complex web forms to submit jobs. These jobs perform complex operations on large sets of cartographic data, and the users have many options to choose from. Users fill out the form in their browser, choosing from dropdowns, entering text, clicking checkboxes, then hitting the “Submit Job” button at the end.
We use a Javascript tool called Formik to accomplish these form functionalities. Formik is very powerful and comes with a toolbelt of utilities such as setting errors and changing field “touched” states (when a user clicks in and out of an input). Formik requires another Javascript tool called Yup that is used to create “schemas” that define and validate the shape of the data.
There is a learning curve to Formik, and it can be tedious to make changes. Because we are using Typescript and because we have decided to require unit tests for the Yup schemas, making just one change to a form can force us to update the Formik form, the typescript types, and the Yup schema, including the error messages and input validation!
Even with this tediousness, I still advocate for using Formik. It’s great for handling forms within a production environment. Combining Formik and Yup creates a well-defined developer experience by providing strongly typed interfaces and autocompletion when writing code.
The alternative to Formik (aside from other libraries) would be to write form state logic handling by hand. This means getting values in and out of form state, validation, and error messages, and handling form submission.
A workflow for using Formik and Yup with Typescript might look like this:
- Add Formik into the form component
- Define the shape of the form data using Typescript
- Set the initial form values as defined by Typescript
- Add the field input
- Create the Yup schema
- Create the unit tests by validating test data against the schema
To use Formik, wrap the <Formik> tag around the entire <form>. Below is an example of a login page of Formik vs a simple login form.
FORMIK EXAMPLE:
export default function LoginFormWithFormik() {
const initialValues = useMemo<LoginFormData>(() => {
const values = new LoginFormData(“”, “”);
return values;
}, [])
const validationSchema = () => {
return Yup.object()…
}
return (
<Formik
initialValues={{ email: “”, password: “” }}
onSubmit={(values) => {
handleSubmit(values)
setSubmitting(false);
}}
validationSchema={validationSchema}
>
{({ errors,
touched,
values, // the form values
isValid,
setFieldValue,
setFieldError,
setFieldTouched,
handleReset,
handleSubmit }) => (
<form>
<EmailInput />
<PasswordInput />
{ errors && <ErrorMessage /> }
</form>
)}
</Formik>
)
}
REACT USESTATE EXAMPLE:
export default function LoginFormWithoutFormik() {
const [email, setEmail] = useState<string>(“”);
const [password, setPassword] = useState<string>(“”);
const [errors, setErrors] = useState<any>({})
const [emailFieldTouched, setEmailFieldTouched] = useState<boolean>(false);
const [passwordFieldTouched, setPasswordFieldTouched] = useState<boolean>(false);
useEffect(() => {
if (!validEmail(email)) {
setErrors()… // set some errors
}
}, [email])
const handleTouched = () => {
… // add some custom touch handling logic
}
const validEmail = (email: string) => {
… // validate email string
}
return (
<form>
<EmailInput onMouseOut={handleTouched} />
<PasswordInput onMouseOut={handleTouched} />
{errors && <ErrorMessage /> }
</form>
)
}
Notice that with Formik, developers automatically get certain utilities out of the box such as handleFieldTouched, isValid, values, etc. In order to get access to those same variables, you would need to create those from scratch. That would mean extra work in setting up the same tools and that may not even end up being as robust as just using Formik.
My advice for potential users would be this: if you need to set up long and complicated form data, embrace the extra work involved in setting up and maintaining Formik. While it can be frustrating at times to get the Yup schema validation working properly, once you get familiar with creating equality checks, regex, and conditional statements, it’s well worth the effort seeing the form work as expected.