1. 程式人生 > >Formik For React: Introduction to Form Management Done Right

Formik For React: Introduction to Form Management Done Right

Installing and Importing Formik

Formik is somewhat of a 2018 success story, with the packing going from 6k weekly downloads at the beginning of the year to surpassing 100k weekly downloads in September, with a growth trajectory still looking healthy now in October.

As you can imagine with the sudden burst of popularity, Formik is in active development as the community push the concept forward and issues are worked through. To explore the source code in it’s entirety, visit the Github project at

https://github.com/jaredpalmer/formik.

Installing Formik adds a Gzipped size of 13.7kb to your project at the time of this writing; a very reasonable size given its capabilities.

Install it with the following npm command:

npm i formik

For this article we will be importing the following Formik components into our project:

import { Formik, FormikProps, Form, Field } from 'formik';

Let’s briefly run down these objects before utilising them to create a form.

  • <Formik /> is our top most component. Through its props we will configure some of Formik’s built in form handlers, which essentially allow our form to function. We will also utilise the render
    prop. Our entire form is constructed via a function passed into this prop.
  • FormikProps is passed through the above render prop function, which gives us access to the state of our form — values, errors, whether an input has been touched (selected for the first time), whether the form is submitting, and so forth.
  • <Form /> is the simplest of our imports here, which we use to simply wrap our fields with. Hence, all <Field /> components must be children of <Form />.
  • <Field /> will be used to make our elements compatible with Formik. In other words, without the Field component, our input elements will know nothing about Formik. <Field /> actually provides an input text box as its default input method, as well as a select dropdown and textarea options.This means we can render a Formik-linked input by just declaring a Field component. However, we can also pass custom components through to link Formik to any input method we wish to use.

If this is hard to visualise, in the next section we will dive into how the form is structured in our JSX markup.

Structuring a Formik Form

Let’s take a look at how to construct a Formik form. Let’s keep our form example simple and deal with 3 fields: first name, email address and gender.

Before jumping into the full implementation, let’s review the structure of the Formik form:

...import { Formik, FormikProps, Form, Field } from 'formik';
export class MyForm extends React.Component {
   handleSubmit = (values, {       props = this.props,       setSubmitting     }) => {       //process form submission here
    //done submitting, set submitting to false    setSubmitting(false);
    return;  }
   render() {         return(      <Formik        initialValues={{            first_name: '',            email_address: '',            gender: ''        }}        validate={(values) => {           let errors = [];                         //check if my values have errors           return errors;        }}        onSubmit={handleSubmit}        render={formProps: FormikProps => {
           return(              <Form>                <Field ... />                <Field ... />                <Field ... />
                <button                   type="submit"                   disabled={formProps.isSubmitting}>                     Submit Form                </button>              </Form>           );       }}      />);   }}

Quite a bit is going on here, even for a simple form! Let’s break down what is going on:

  • My entire Formik form is housed in a component called MyForm.
  • I am firstly defining the form submission handler, which will later be passed to a Formik prop. Notice the arguments of this function. the first is values: this contains all my form values to work with. The second argument is an object, allowing us to pass our props and Formik methods into our submission handler. setSubmitting allows us to tell Formik whether the form is submitting or not, as a boolean value.
  • The entire form is rendered in our component’s render function. Nothing new here.
  • Within render, we are configuring a <Formik /> component, and nothing else.
  • Notice how our form markup itself is configured in the render prop of <Formik />. The Formik object also includes props for initial values, validation, and of course, onSubmit and render.

This is the general pattern of a Formik form — we pass our functionalities through props with the help of Formik objects behind the scenes that do a great job of organising our form values and errors, and other things outside the scope of our first example!

When constructing forms, the last thing we want to worry about is how to juggle all our state, with errors, values and what not. With Formik we do not have to — it allows us to focus on our form components and the handling of its interaction.

Configuring a Formik <Field />

Each of our form elements need to be accompanied with a <Field /> component. By default we can use the type="text" prop to render a text input.

This is what that may look like, with some extra functionality we will shortly address:

<Field   type="text"   name="email"   placeholder="Email Address"   className='textbox'  />     { formProps.touched.email &&    formProps.errors.email &&       <span className='error'>{formProps.errors.email}</span>  }

So we have defined our first field using Formik. The <Field /> component accepts props we might expect a vanilla input element to accept, such as a class, name, type and placeholder. However in React we have state to deal with.

What Formik does is hook up our input values to state using its name prop. In the above example, the state value will be stored as values.email.

But notice the code below <Field />. This code deals with displaying an error for this particular field. (The error is taken from our formProps object, which Formik automatically updates for us — all you need to do is remember to pass formProps inside the render prop function).

We only display this error if the email field has been touched, and if the error exists — then we access it via formProps.errors.email.

However…

There is a better way to display this error, using Formik’s <ErrorMessage /> object.

The <ErrorMessage /> Object

The ErrorMessage object only displays the error if the field has been touched, (or intereacted with). Instead of writing this (from above):

{ formProps.touched.email &&    formProps.errors.email &&       <span className='error'>{formProps.errors.email}</span>  }

We can simply write this:

<ErrorMessage name="email" />

Much neater syntax.

Note: Remember to import ErrorMessage within your Formik import.

So in order to generate our errors, we use Formik’s validate prop.This leads us nicely onto validation.

Configuring Validation

Where did our email error value come from? Remember our validate prop that we defined in the <Formik /> object? Here it is again:

<Formik    validate={(values) => {      let errors = {};                     //check if my values have errors       return errors;   }}   .../>

As you can see, we can define our errors in an object, based on our form values, and return the errors when we are done. From here Formik will pick these up and place them in formProps. Like formProps, our validate values argument is also managed by Formik — you just need to remember to pass it in this validate function.

Now, we have 2 options for validation; the first is to use our validate prop as we have been discussing above, where we may do something like this:

<Formik  validate={(values) => {     let errors = {};
      if(!values.email || is_valid_email(values.email))         errors.email = 'Valid Email Address Required';                    return errors;  }}  .../>

We can also adopt any third party library to use as our means of validation using this method.

The second method is by using Yup. We mentioned Yup near the beginning of this article, and this is why. We could indeed carry out Yup validation within our validate prop, however, Formik is now supporting a validationSchema prop to automatically validates your form based on a Yup object.

So we could do something like this instead:

//define my schema using Yupconst const formSchema = yup.object().shape({   ...});
<Formik  validationSchema={formSchema}  .../>

Note: Remember to import yup, with import * as yup from ‘yup’;

As you can see, using validationShema and Yup is a lot simpler and easier to read and manage! However — if you indeed need further functionality, maybe an API request or websocket to validate an available username, we always have the validate prop at our disposal.

Check out this guide to dive deeper into validation with Formik.

Abstracting your handlers

At this point it is worth mentioning how you present your handlers in your projects.

Small forms can be defined as shown above, but as they grow your handler functions may need to be abstracted or refactored to keep your project organised.

For this reason, it is good practice to define your Formik handler functions outside of the <Formik /> object itself.

Taking our previous example, we could redefine the handlers in our component, or import them as a module — it is up to you to decide how to best structure your project, based on your design patterns.

Here is what importing your functions may look like:

import * as myForm from './forms/myForm';
export class MyForm extends React.Component {
  render() {          return(       <Formik         initialValues={myForm.initialValues}         validate={myForm.validate}         onSubmit={myForm.onSubmit}         render={           (formProps: FormikProps) => {                            <Form>                <Field ... />                ...              </Form>         }}      />    );  }}

This makes things more readable, giving the majority of screen real-estate to the form itself. If your component is hosting a multitude of forms, then it may be worth considering refactoring the form markup itself in a more abstract way too.

Using Other Input types with Field

We can use other input types with Fields. In fact, the component prop of <Field /> can accept input, select and textarea, as strings. We can also pass components into this prop, consequently allowing us to render any component we wish.

The same fieldProps object mentioned above, plus any other props, can be passed directly to <Field />.

As an example, defining a select dropdown with Field may look like the following:

<Field  name="gender"   component="select"   placeholder="Your Gender">        <option value="male">Male</option>     <option value="female">Female</option></Field>

To save repetition in this article, refer to the Field API reference for more information on configuring Fields, and how to pass custom fields.

So now we are getting somewhere. We now know:

  • What Formik is, and how to structure a Formik form.
  • We also know how to use <Form /> and <Field />to define our form fields, and that our form handlers are passed in the <Formik /> component.
  • The Formik component deals with a whole lot of magic objects for us, that all we need to do is remember to pass them into our handlers.
  • Validation can be done manually via the validate prop, or with Yup exclusively using validationSchema. Either way, we can utilise third party packages within Formik to carry out our validation.

Formik admittedly is not a simple package that you can simply jump into in 5 minutes — which reflects the verboseness of the documentation online. Although we have covered the fundamentals of how Formik works, we have left out a lot of functionality the package offers to us.

A Note on FieldArrays

Another handy Formik object is the <FieldArray />.

The <FieldArray /> object helps us manage forms that adopt an iterable list of inputs with a common subject.

This method will allow us to loop through a range of values and construct Fields for each of them. We also have access to Formik’s arrayHelpers object, which we can utilise to add and remove items to the array of fields we are managing. This functionality becomes super useful when you have a list of things to configure, like tags of a post, or a list of product attributes, etc.

Understanding what we have discussed in this article is a prerequisite for using FieldArrays. When you are ready, dive into my Field Array focussed article:

Summing Up

It is worth mentioning also that Formik is under active development, so make sure to refer to the documentation for updates, the overview of which is located at the following URL:

Note: Expect more articles from me in the future on the more advanced functions of Formik.

I have included a gist of our example form discussed above to act as an entry into Formik. Feel free to copy it into your projects to start building your own Formik forms: