Formik validations with contexts

Formik is one of the most popular libraries to handle forms in react applications. Since a couple of months, we are able to download, long awaited the second version of it. Besides of fixes, authors provide us a lot of new stuff. One of them is hook useFormikContext(), which opens a lot of new doors to extending our forms.

At the very beginning, let’s take a look at simple form, implemented with Formik:

import React from 'react'
import { Formik, Field, Form } from 'formik'
import { object, string } from 'yup'

export const validationSchema = () => object().shape({
  login: string().required().email(),
  password: string().required(),
})

const LoginForm = () => (
  <div>
    <h1>Login Form</h1>
    <Formik
      initialValues={{ email: '' }}
      onSubmit={(values, actions) => {
        actions.props.login(values)
      }}
      validationSchema={validationSchema}
      render={(props: FormikProps<Values>) => (
        <Form>
          <Field type="email" name="email" placeholder="Email" />
          <Field type="password" name="password" />
          <button type="submit">Submit</button>
        </Form>
      )}
    />
  </div>
)

Code above is very simple login form, with email and password validation (handled by Yup). Unfortunately, this code won’t show any error, which is a bit lame, from UX point of view.

In newest version, Formik has its own, dedicated component for such thing: <ErrorMessage />, which can handle showing error messages based on validation schema passed to form. Everything, what we have to do is to pass field name as a props. It will check if form has any error on this field and show the last one. It’s cool solution if you won’t check if field was touched. Unfortunately, this component doesn’t check that.

But don’t be sad. We can do such thing on our own, with help from useFormikContext() hook! Let’s prepare our version of ErrorMessage component:

import { useFormikContext } from 'formik'
import * as React from 'react'

export const ErrorMessage = ({ name }) => {
  const { errors, touched } = useFormikContext()

  return errors[name] && touched[name]
    ? <div>{errorMessage}</div>
    : null
}

As you can see, useFormikContext() gives us an access to all props of our form from nested component level. But there is one condition: nested component has to be child of <Formik /> component or be wrapped by whitFormik() HOC. So, we don’t have to pass touched or error props to each nested components, just to check if they exists for this particular field. Right now we have full access, thanks for hook, to all props of forms. According to this, we can better split responsibilities in our form.

Finally, let’s take a look, how our form looks after our changes:

import React from 'react'
import { Formik, Field, Form } from 'formik'
import { object, string } from 'yup'
import { ErrorMessage } from '../ErrorMessage'

export const validationSchema = () => object().shape({
  login: string().required().email(),
  password: string().required(),
})

const LoginForm = () => (
  <div>
    <h1>Login Form</h1>
    <Formik
      initialValues={{ email: '' }}
      onSubmit={(values, actions) => {
        actions.props.login(values)
      }}
      validationSchema={validationSchema}
      render={(props: FormikProps<Values>) => (
        <Form>
          <Field type="email" name="email" placeholder="Email" />
          <ErrorMessage name='email' />
          <Field type="password" name="password" />
          <ErrorMessage name='password' />
          <button type="submit">Submit</button>
        </Form>
      )}
    />
  </div>
)

Summarizing: useformikContext() is blessing from 2.x version of Formik. With its help we can get all form’s props in nested components. It’s very useful in case of creating custom solutions for validations, labels or other generic components working on form’s attributes.

Share This: