Formik onsubmit error

is a component that helps you with building forms. It uses a render props pattern made popular by libraries like React Motion and React Router.

<Formik> is a component that helps you with building forms. It uses a render
props pattern made popular by libraries like React Motion and React Router.

Example

import React from 'react';

import { Formik } from 'formik';

const BasicExample = () => (

<div>

<h1>My Form</h1>

<Formik

initialValues={{ name: 'jared' }}

onSubmit={(values, actions) => {

setTimeout(() => {

alert(JSON.stringify(values, null, 2));

actions.setSubmitting(false);

}, 1000);

}}

>

{props => (

<form onSubmit={props.handleSubmit}>

<input

type="text"

onChange={props.handleChange}

onBlur={props.handleBlur}

value={props.values.name}

name="name"

/>

{props.errors.name && <div id="feedback">{props.errors.name}</div>}

<button type="submit">Submit</button>

</form>

)}

</Formik>

</div>

);

Props


Reference

Props

Formik render methods and props

There are 2 ways to render things with <Formik />

  • <Formik component>
  • <Formik children>
  • <Formik render> Deprecated in 2.x

Each render methods will be passed the same props:

dirty: boolean

Returns true if values are not deeply equal from initial values, false otherwise.
dirty is a readonly computed property and should not be mutated directly.

errors: { [field: string]: string }

Form validation errors. Should match the shape of your form’s values defined
in initialValues. If you are using validationSchema (which you should be),
keys and shape will match your schema exactly. Internally, Formik transforms raw
Yup validation errors
on your behalf. If you are using validate, then that function will determine
the errors objects shape.

handleBlur: (e: any) => void

onBlur event handler. Useful for when you need to track whether an input has
been touched or not. This should be passed to <input onBlur={handleBlur} ... />

handleChange: (e: React.ChangeEvent<any>) => void

General input change event handler. This will update the values[key] where
key is the event-emitting input’s name attribute. If the name attribute is
not present, handleChange will look for an input’s id attribute. Note:
«input» here means all HTML inputs.

handleReset: () => void

Reset handler. Will reset the form to its initial state. This should be passed
to <button onClick={handleReset}>...</button>

handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void

Submit handler. This should be passed to <form onSubmit={props.handleSubmit}>...</form>. To learn more about the submission process, see Form Submission.

isSubmitting: boolean

Submitting state of the form. Returns true if submission is in progress and false otherwise. IMPORTANT: Formik will set this to true as soon as submission is attempted. To learn more about the submission process, see Form Submission.

isValid: boolean

Returns true if there are no errors (i.e. the errors object is empty) and false otherwise.

Note: isInitialValid was deprecated in 2.x. However, for backwards compatibility, if the isInitialValid prop is specified, isValid will return true if the there are no errors, or the result of isInitialValid of the form if it is in «pristine» condition (i.e. not dirty).

isValidating: boolean

Returns true if Formik is running validation during submission, or by calling [validateForm] directly false otherwise. To learn more about what happens with isValidating during the submission process, see Form Submission.

resetForm: (nextState?: Partial<FormikState<Values>>) => void

Imperatively reset the form. The only (optional) argument, nextState, is an object on which any of these FormikState fields are optional:

interface FormikState<Values> {

values: Values;

errors: FormikErrors<Values>;

touched: FormikTouched<Values>;

isSubmitting: boolean;

isValidating: boolean;

status?: any;

submitCount: number;

}

If nextState is specified, Formik will set nextState.values as the new «initial state» and use the related values of nextState to update the form’s initialValues as well as initialTouched, initialStatus, initialErrors. This is useful for altering the initial state (i.e. «base») of the form after changes have been made.

function MyForm(props: MyFormProps) {

return (

<Formik<Blog>

initialValues={props.initVals}

onSubmit={(values, actions) => {

props.onSubmit(values).then(() => {

actions.setSubmitting(false);

actions.resetForm({

values: {

title: '',

image: '',

body: '',

},

});

});

}}

>

</Formik>

);

}

If nextState is omitted, then Formik will reset state to the original initial state. The latter is useful for calling resetForm within componentDidUpdate or useEffect.

setErrors: (fields: { [field: string]: string }) => void

Set errors imperatively.

setFieldError: (field: string, errorMsg: string) => void

Set the error message of a field imperatively. field should match the key of
errors you wish to update. Useful for creating custom input error handlers.

setFieldTouched: (field: string, isTouched?: boolean, shouldValidate?: boolean) => void

Set the touched state of a field imperatively. field should match the key of
touched you wish to update. Useful for creating custom input blur handlers. Calling this method will trigger validation to run if validateOnBlur is set to true (which it is by default). isTouched defaults to true if not specified. You can also explicitly prevent/skip validation by passing a third argument as false.

submitForm: () => Promise

Trigger a form submission. The promise will be rejected if form is invalid.

submitCount: number

Number of times user tried to submit the form. Increases when handleSubmit is called, resets after calling
handleReset. submitCount is readonly computed property and should not be mutated directly.

setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void

Set the value of a field imperatively. field should match the key of
values you wish to update. Useful for creating custom input change handlers. Calling this will trigger validation to run if validateOnChange is set to true (which it is by default). You can also explicitly prevent/skip validation by passing a third argument as false.

setStatus: (status?: any) => void

Set a top-level status to anything you want imperatively. Useful for
controlling arbitrary top-level state related to your form. For example, you can
use it to pass API responses back into your component in handleSubmit.

setSubmitting: (isSubmitting: boolean) => void

Set isSubmitting imperatively. You would call it with setSubmitting(false) in your onSubmit handler to finish the cycle. To learn more about the submission process, see Form Submission.

setTouched: (fields: { [field: string]: boolean }, shouldValidate?: boolean) => void

Set touched imperatively. Calling this will trigger validation to run if validateOnBlur is set to true (which it is by default). You can also explicitly prevent/skip validation by passing a second argument as false.

setValues: (fields: React.SetStateAction<{ [field: string]: any }>, shouldValidate?: boolean) => void

Set values imperatively. Calling this will trigger validation to run if validateOnChange is set to true (which it is by default). You can also explicitly prevent/skip validation by passing a second argument as false.

status?: any

A top-level status object that you can use to represent form state that can’t
otherwise be expressed/stored with other methods. This is useful for capturing
and passing through API responses to your inner component.

status should only be modified by calling
setStatus.

touched: { [field: string]: boolean }

Touched fields. Each key corresponds to a field that has been touched/visited.

values: { [field: string]: any }

Your form’s values. Will have the shape of the result of mapPropsToValues
(if specified) or all props that are not functions passed to your wrapped
component.

validateForm: (values?: any) => Promise<FormikErrors<Values>>

Imperatively call your validate or validateSchema depending on what was specified. You can optionally pass values to validate against and this modify Formik state accordingly, otherwise this will use the current values of the form.

validateField: (field: string) => void

Imperatively call field’s validate function if specified for given field or run schema validation using Yup’s schema.validateAt and the provided top-level validationSchema prop. Formik will use the current field value.

component?: React.ComponentType<FormikProps<Values>>

<Formik component={ContactForm} />;

const ContactForm = ({

handleSubmit,

handleChange,

handleBlur,

values,

errors,

}) => (

<form onSubmit={handleSubmit}>

<input

type="text"

onChange={handleChange}

onBlur={handleBlur}

value={values.name}

name="name"

/>

{errors.name && <div>{errors.name}</div>}

<button type="submit">Submit</button>

</form>

);

Warning: <Formik component> takes precendence over <Formik render> so
don’t use both in the same <Formik>.

render: (props: FormikProps<Values>) => ReactNode

Deprecated in 2.x

<Formik render={props => <ContactForm {...props} />} />

<Formik

render={({ handleSubmit, handleChange, handleBlur, values, errors }) => (

<form onSubmit={handleSubmit}>

<input

type="text"

onChange={handleChange}

onBlur={handleBlur}

value={values.name}

name="name"

/>

{errors.name &&

<div>

{errors.name}

</div>}

<button type="submit">Submit</button>

</form>

)}

/>

children?: React.ReactNode | (props: FormikProps<Values>) => ReactNode

<Formik children={props => <ContactForm {...props} />} />

<Formik>

{({ handleSubmit, handleChange, handleBlur, values, errors }) => (

<form onSubmit={handleSubmit}>

<input

type="text"

onChange={handleChange}

onBlur={handleBlur}

value={values.name}

name="name"

/>

{errors.name &&

<div>

{errors.name}

</div>}

<button type="submit">Submit</button>

</form>

)}

</Formik>

enableReinitialize?: boolean

Default is false. Control whether Formik should reset the form if
initialValues changes (using deep equality).

isInitialValid?: boolean

Deprecated in 2.x, use initialErrors instead

Control the initial value of isValid prop prior to
mount. You can also pass a function. Useful for situations when you want to
enable/disable a submit and reset buttons on initial mount.

initialErrors?: FormikErrors<Values>

Initial field errors of the form, Formik will make these values available to
render methods component as errors.

Note: initialErrors is not available to the higher-order component withFormik, use
mapPropsToErrors instead.

initialStatus?: any

An arbitrary value for the initial status of the form. If the form is reset, this value will be restored.

Note: initialStatus is not available to the higher-order component withFormik, use
mapPropsToStatus instead.

initialTouched?: FormikTouched<Values>

Initial visited fields of the form, Formik will make these values available to
render methods component as touched.

Note: initialTouched is not available to the higher-order component withFormik, use
mapPropsToTouched instead.

initialValues: Values

Initial field values of the form, Formik will make these values available to
render methods component as values.

Even if your form is empty by default, you must initialize all fields with
initial values otherwise React will throw an error saying that you have changed
an input from uncontrolled to controlled.

Note: initialValues not available to the higher-order component, use
mapPropsToValues instead.

onReset?: (values: Values, formikBag: FormikBag) => void

Your optional form reset handler. It is passed your forms values and the
«FormikBag».

onSubmit: (values: Values, formikBag: FormikBag) => void | Promise<any>

Your form submission handler. It is passed your forms values and the
«FormikBag», which includes an object containing a subset of the
injected props and methods (i.e. all the methods
with names that start with set<Thing> + resetForm) and any props that were
passed to the wrapped component.

Note: errors, touched, status and all event handlers are NOT
included in the FormikBag.

IMPORTANT: If onSubmit is async, then Formik will automatically set isSubmitting to false on your behalf once it has resolved. This means you do NOT need to call formikBag.setSubmitting(false) manually. However, if your onSubmit function is synchronous, then you need to call setSubmitting(false) on your own.

validate?: (values: Values) => FormikErrors<Values> | Promise<any>

Note: I suggest using validationSchema and Yup for validation. However,
validate is a dependency-free, straightforward way to validate your forms.

Validate the form’s values with function. This function can either be:

  1. Synchronous and return an errors object.

const validate = values => {

const errors = {};

if (!values.email) {

errors.email = 'Required';

} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}$/i.test(values.email)) {

errors.email = 'Invalid email address';

}

return errors;

};

  • Asynchronous and return a Promise that’s resolves to an object containing errors

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

const validate = values => {

return sleep(2000).then(() => {

const errors = {};

if (['admin', 'null', 'god'].includes(values.username)) {

errors.username = 'Nice try';

}

return errors;

});

};

validateOnBlur?: boolean

Default is true. Use this option to run validations on blur events. More
specifically, when either handleBlur, setFieldTouched, or setTouched
are called.

validateOnChange?: boolean

Default is true. Use this option to tell Formik to run validations on change
events and change-related methods. More specifically, when either
handleChange, setFieldValue, or setValues are called.

validateOnMount?: boolean

Default is false. Use this option to tell Formik to run validations when the <Formik /> component mounts
and/or initialValues change.

validationSchema?: Schema | (() => Schema)

A Yup schema or a function that returns a Yup
schema. This is used for validation. Errors are mapped by key to the inner
component’s errors. Its keys should match those of values.

I am creating a form by using react and formik.Below is my code:

<div>
  <Formik
    initialValues={{
      email: ""
    }}
    onSubmit={(values: FState, setSubmitting: any) => {
      console.log("Enter in submit function", values);
      setTimeout(() => {
        alert(JSON.stringify(values, null, 2));
        setSubmitting(false);
      }, 500);
    }}
    validationSchema={validationSchemaGlobal}
  >
    {({
      errors,
      touched,
      handleBlur,
      handleChange,
      isSubmitting,
      values,
      handleSubmit
    }: any) => (
      <div>
        <Cards>
          <form onSubmit={handleSubmit}>
            <CardBody>
              <h4>
                Enter Your Email Address and we'll send you a link to reset your
                password
              </h4>
              <Field
                type="text"
                id="email"
                value={values.email}
                component={CustomInput}
                onChange={handleChange}
                onBlur={handleBlur}
              />
              {errors.email && touched.email ? (
                <div style={{ color: "red" }}>{errors.email}</div>
              ) : null}
            </CardBody>
            <CardFooter>
              <br />
              <button type="submit" disabled={isSubmitting}>
                Send Password Reset Link
                {/* {isSubmitting && <i className="fa fa-sponner fa-spin"/>} */}
              </button>
            </CardFooter>
          </form>
        </Cards>
      </div>
    )}
  </Formik>
</div>

In this formik form, onSubmit function not working. I dont know why? Please tell me guys what is problem with my code?

Tholle's user avatar

Tholle

104k19 gold badges190 silver badges180 bronze badges

asked Mar 19, 2019 at 9:25

NewCoder's user avatar

6

Check your validationSchema. I ran into this problem and found that my validator was returning something that signaled to Formik the form was invalid, but no other warnings or messages were coming up. It just wouldn’t submit.

Replace that prop with validator={() => ({})} i.e. just an empty object being returned. That should pass validation and trigger your onSubmit. You can restore your functionality from there.

  <Formik
    initialValues={{
      email: ""
    }}
    onSubmit={() => { console.log("submit!"); }}
    validator={() => ({})}
  >
    {/* */}
  </Formik>

answered Sep 27, 2019 at 22:21

Chris's user avatar

ChrisChris

6,4303 gold badges34 silver badges48 bronze badges

5

In my case I use Yup as validator and I accidentally had firstName and lastName in my validationSchema as required but I did not have those values in my form.

My validationSchema was,

const SignupSchema = Yup.object().shape({
  firstName: Yup.string()
    .min(2, 'Too Short!')
    .max(50, 'Too Long!')
    .required('Required'),
  lastName: Yup.string()
    .min(2, 'Too Short!')
    .max(50, 'Too Long!')
    .required('Required'),
  email: Yup.string()
    .email('Invalid email')
    .required('Required'),
  password: Yup.string()
    .min(6, 'Password must be at least 6 characters')
    .max(24, 'Password can be maximum 24 characters')
    .required('Required')
}) 

I just deleted firstName and lastName,

const SignupSchema = Yup.object().shape({
  email: Yup.string()
    .email('Invalid email')
    .required('Required'),
  password: Yup.string()
    .min(6, 'Password must be at least 6 characters')
    .max(24, 'Password can be maximum 24 characters')
    .required('Required')
})

So check your validationSchema and see if you require something that does not exist in your form.

answered Jun 1, 2020 at 2:13

Hasan Sefa Ozalp's user avatar

Hasan Sefa OzalpHasan Sefa Ozalp

5,6893 gold badges31 silver badges43 bronze badges

5

I imported Form from react-bootstrap instead of formik, so I was having this issue. The issue was solved by importing the Form of formik. Sometimes, directly using Form.Control of react-bootstrap instead of Field of formik also gives this issue.

If you really have to use Form.Control you can use render prop.

answered Aug 8, 2021 at 6:56

Bhuwan Adhikari's user avatar

Bhuwan AdhikariBhuwan Adhikari

7411 gold badge8 silver badges18 bronze badges

2

A little bit late for the original question but I experienced the same issue and solved it easy but hard to find.

When I passed the «name» prop to the component I had written «DateOfBirth» instead of with lowercase, which means it didn’t match my validationSchema.

My schema looks like this:

export const userSchema = yup.object().shape({
firstName: yup.string().min(1).max(50).required('Field is required'), 
lastName: yup.string().min(1).max(50).required('Field is required'), 
dateOfBirth: yup.date().required('Invalid input'),});

This menas the name of the component has to match

Before (Didn’t work):

 <DateTimePicker name="DateOfBirth" label="Date of birth" />

After (Worked):

<DateTimePicker name="dateOfBirth" label="Date of birth" />

answered Jun 8, 2021 at 6:33

Huezzer's user avatar

HuezzerHuezzer

1161 silver badge4 bronze badges

1

In my case, onSubmit was not working because I forgot to wrap my form in the <form></form> tag. A stupid issue, but it can be the reason for this behavior. If the above solutions don’t work, check that you have the form tag.

answered Apr 16, 2021 at 14:02

Anna Logacheva's user avatar

Anna LogachevaAnna Logacheva

6421 gold badge8 silver badges16 bronze badges

1

In my case, mistakenly I have passed validationSchema to wrong prop.

Error:

  <Formik
    initialValues={initialValues}
    validate={validationSchema} <----- Error
  >

Proper way:

  <Formik
    initialValues={initialValues}
    validationSchema={validationSchema} <----- Good
  >

answered Sep 19, 2022 at 12:50

Aga's user avatar

AgaAga

7296 silver badges16 bronze badges

This may happen because the form is being submitted but it is invalid , this may happen because the validation schema is not matching ur form for more than one reason ,

in my case , it was because there was a string , and it is been sent as null , so I just added .nullable() to the validation schema for that field.

answered Mar 21, 2022 at 12:32

rawand deheliah's user avatar

Had extra field in my validationSchema which was declared with Yup. however that field wasn’t declared in Formik hence it didn’t work. After removing the field from validationSchema, it works.

answered Oct 23, 2022 at 21:47

Sher Sanginov's user avatar

I am mentioning one more possibility through which i handled.
change the button type and add onClick like this

<Button type="button" onClick={submitForm}>

also add submitForm prop at top along with values, touched etc

 {({ submitForm, errors, handleChange, handleSubmit, touched, values }) => (

now its working

answered Apr 29, 2022 at 9:32

saud00's user avatar

saud00saud00

3933 silver badges13 bronze badges

My mistake was I was not initializing error with blank on validation

 const errors:any={};

Here is full code for login form, check the validate function

    <Formik
     initialValues={{ email: "", password: "" }}
     validate={(formValues) => {
         const errors:any={};
         if (!formValues.email) {
             errors.email = "Invalid email";
         }
         if (!formValues.password) {
             errors.password = "Password is required";
         }
         return errors;

     }}
     onSubmit={async (values) => {
         console.log("submit", values);
         dispatch(login({ username: values.email, password: values.password }));
         if (loginState.isError) {
             alert(loginState.message);
         }
     }}
 >{({ values, handleChange, errors, dirty, isValid, isSubmitting, handleSubmit, setFieldValue, setFieldTouched, setFieldError }) => (
     <Form onSubmit={handleSubmit}>
         <FormGroup>
             <Label>Email</Label>
             <Input type="email" name="email" valid={!isEmpty(errors.email)} value={values.email} onChange={handleChange}></Input>
             <FormFeedback className="font-weight-bold" type="invalid" role="alert"> {errors.email}</FormFeedback>
         </FormGroup>
         <FormGroup>
             <Label>Password</Label>
             <Input type="password" name="password" value={values.password} valid={!isEmpty(errors.password)} onChange={handleChange}></Input>
             <FormFeedback className="font-weight-bold" type="invalid" role="alert"> {errors.password}</FormFeedback>

         </FormGroup>
         <FormGroup className="text-center">
         <p> {isValid === true ? "is valid" : "not valid"} </p>
             <Button type="submit" color="primary" className="mt-3">Login</Button>
         </FormGroup>
     </Form>
 )}
 </Formik>

answered Jan 23, 2022 at 20:33

Nishant Kumar Verma's user avatar

I solved this because I declared the onsubmit function without the const word (I know it’s stupid)

answered Mar 25, 2022 at 16:02

Farouk Elayache's user avatar

1

I was having the same issue. My onSubmit function was not executing onClick on submit button.

The problem was in Yup.validation schema. There was an extra field that I did not use. I remove that field and boom.

answered Jan 3 at 15:58

Abdur Rahman's user avatar

Posting this as I had the same pronlem and the problem was even different:

My validation function was returning an errors object that always contained all fields, all with empty strings when they were correct.

Form submission seems disabled when the errors object is not empty.

answered Jan 29 at 7:40

Cazlab's user avatar

1

Eu resolvi esse problema criando uma interface com as propriedades como não obrigatórias e setei a const error como sendo dessa interface.

interface IErrorList {
  firstName?: string;
  lastName?: string;
  streetLine?: string;
  streetLine2?: string;
  city?: string;
  state?: string;
}

e a função ficou assim

  const FormValidade = (values: TInitValues) => {
    const errors: IErrorList = {};

    if (!values.firstName) {
      errors.firstName = "Required";
    }

    if (!values.lastName) {
      errors.lastName = "Required";
    }
    if (!values.streetLine) {
      errors.streetLine = "Required";
    }
    if (!values.city) {
      errors.city = "Required";
    }
    if (!values.state) {
      errors.state = "Required";
    }
    return errors;
  };

answered yesterday

Francisco Clavero's user avatar

Use instead of button tag as i worked for me.

answered May 18, 2021 at 7:05

Raja Boddu's user avatar

1

We all know that making and maintaining forms in React can be a bit of a pain, thankfully Formik is here to save us.

In this post we will go over how to use Formik in React, why you should use it, and when you should and shouldn’t use it.

I feel that before we get into how to use Formik, we first need to talk about why it is important, rather than just putting your form into a useState or useReducer react hook.

For those of you who just want to see how to use Formik in React, click here to skip to that part.

As with all my posts, I aim to write this post without too much technical jargon so that all experience levels can follow along and benefit from it.

Why use Formik?

Well let’s start by looking at the alternatives (although we won’t be considering other libraries here).

Option 1: Create a form using useState

Using the useState react hook is probably the fastest way you can implement a basic form in react. You can either put everything into a single state using an object or you can create multiple states each with their own form field.

If you were to go down this route, adding your form fields into a single state with the use of an object would probably best to avoid polluting your codebase with new useState hooks.

To do this you could create a helper method to manage adding, updating and removing the fields.

Whilst this might be fast to implement, it will become fairly un-maintainable pretty quickly and you will have to manually add all the onChange handlers to each and every field not to mention side effects, error handling and so on.

Option 2: Create a form using useReducer

Using the useReducer react hook is pretty much identical to the useState hook except that you can manage the form state a little better with more control rather than having to create a helper to update the state.

Option 3: Create your own form component/library

Finally this last option might be useful if you have a really bespoke need that something like Formik cannot solve for you, but for the most part this will just be re-inventing the wheel and you will spend a lot more time building out your form components/library, when you could instead use that time to read over the Formik docs.

Option 4: Creating your form using your redux store

This is probably the worst option of them all and there are very few occasions when this will ever be necessary.

It is a bad choice because when you use redux with a store as your single source of truth, the last thing you want to do is pollute it with single use form fields, and properties that either may never get used, or will only ever be used once and never need to be persisted across your application.

In this situation, once again having a local form state (like Formik) would work in your favour, and after the form gets submitted, you can then fire some actions with the resulting data so you only save what you need.

For the most part, these four options are not ideal and when you compare them with a plug and play library like Formik. You can start to see why you will want to go with Formik 99% of the time.

With that being said there are occasions when you might want to use other options which we will go over a bit later on.

How to use Formik in React?

So how do you use Formik in React?

It is pretty straightforward to get started with.

I won’t go over everything in Formik but if you want to have a read over the whole API then check out the official Formik docs here.

Step 1. Install

If you are using yarn, start by installing it into your project with:

And if you are using NPM:

npm install Formik --save

Once we have that installed we are going to create a basic component that uses Formik with a few form fields to initialise the form and tell Formik what we need:

import React from "react"
import { Formik } from "formik"

function buyApples(applesAmount) {
  alert(`You have purchased ${applesAmount} apples`)
}

export default function ExampleFormikForm() {
  return (
    <Formik
      initialValues={{
        applesAmount: 0,
      }}
      onSubmit={values => {
        buyApples(values.applesAmount)
      }}
    >
      {({ handleChange, handleBlur, values, handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <label htmlFor="applesAmount">
            <span>Amount of apples:</span>
            <input
              name="applesAmount"
              type="number"
              onChange={handleChange}
              onBlur={handleBlur}
              value={values.applesAmount}
              required
            />
          </label>
          <button type="submit">Buy apples</button>
        </form>
      )}
    </Formik>
  )
}

As you can see we have our component which contains our Formik form which has an input where you can enter the number of apples you want to buy.

Aside from that we have a submit button as well as a function buyApples which gets called when the form is submitted successfully.

From Formik we are making use of handleChange, handleBlur, values, and handleSubmit which are all fairly self explanitory.

Basic Formik form example in react

When a user enters a number and then submits, it triggers our buy apples function and displays an alert.

Basic Formik form result example in react

Form Validation:

Now we have our basic form, we need some simple validation on the data we send. To do this we are going to use a library called yup.

Install Yup by running yarn add yup or npm install yup --save just like we did with Formik.

Now lets add our basic validation:

import * as yup from "yup"

const appleFormValidation = yup.object().shape({
  applesAmount: yup.number().required().positive().integer(),
})

As you can see here we are using yup to define our form validation by saying that our applesAmount must be a positive whole number that is required.

Let’s look at how we can now add this into our form:

import React from "react"
import { Formik } from "formik"
import * as yup from "yup"

function buyApples(applesAmount) {
  alert(`You have purchased ${applesAmount} apples`)
}

const appleFormValidation = yup.object().shape({
  applesAmount: yup.number().required().positive().integer(),
})

export default function ExampleFormikForm() {
  return (
    <Formik
      initialValues={{
        applesAmount: 0,
      }}
      validationSchema={appleFormValidation}
      onSubmit={values => {
        buyApples(values.applesAmount)
      }}
    >
      {({ handleChange, handleBlur, values, handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <label htmlFor="applesAmount">
            <span>Amount of apples:</span>
            <input
              name="applesAmount"
              type="number"
              onChange={handleChange}
              onBlur={handleBlur}
              value={values.applesAmount}
              required
            />
          </label>
          <button type="submit">Buy apples</button>
        </form>
      )}
    </Formik>
  )
}

Here we are only adding in one prop into Formik which is validationSchema={appleFormValidation} that applies our validation to the form.

If we now try to enter a string or a negative number our buyApples function will never be called and we will never see the alert.

This means our validation is working!

And there we have our basic form with validation!

As you can see it is really easy to set up and to create forms with.

The next thing we need to look into is handling errors so it doesnt just scilently fail like in the above.

How to handle errors with Formik in react:

Handling errors in Formik is nice and easy, Formik provides an easy to use prop called errors.

All we need to do is check whether an error exists, and if it does then display it to the user.

To do this we first need to get the following properties from our Formik props: errors, touched

And then, we need to add in the following code to our form (just above our input label):

{
  errors.applesAmount && touched.applesAmount && (
    <p style={{ color: "red" }}> {errors.applesAmount} </p>
  )
}

Here is this our error handling code integrated with our Formik form:

import React from "react"
import { Formik } from "formik"
import * as yup from "yup"

function buyApples(applesAmount) {
  alert(`You have purchased ${applesAmount} apples`)
}

const appleFormValidation = yup.object().shape({
  applesAmount: yup.number().required().positive().integer(),
})

export default function ExampleFormikForm() {
  return (
    <Formik
      initialValues={{
        applesAmount: 0,
      }}
      validationSchema={appleFormValidation}
      onSubmit={values => {
        buyApples(values.applesAmount)
      }}
    >
      {({
        handleChange,
        handleBlur,
        values,
        handleSubmit,
        errors,
        touched,
      }) => (
        <form onSubmit={handleSubmit}>
          {errors.applesAmount && touched.applesAmount && (
            <p style={{ color: "red" }}> {errors.applesAmount} </p>
          )}
          <label htmlFor="applesAmount">
            <span>Amount of apples:</span>
            <input
              name="applesAmount"
              type="number"
              onChange={handleChange}
              onBlur={handleBlur}
              value={values.applesAmount}
            />
          </label>
          <button type="submit">Buy apples</button>
        </form>
      )}
    </Formik>
  )
}

Basic Formik form example with error handling in react

As you can see that only took a few seconds to add in. But we can do this better by making our own error handling component in combination with the useFormikContext hook (don’t worry if you are not familiar with hooks in Formik because we will go into them a little later on).

If we do this we can create a reusable component that we can plug and play in any Formik form.

Here is a basic example of how you could create and use this component:

import React from "react"
import { useFormikContext } from "formik"

export default function HandleError({ name }) {
  const { errors, touched } = useFormikContext()

  if (errors?.[name] && touched?.[name]) {
    return <p style={{ color: "red" }}> {errors[name]} </p>
  }

  return null
}

And in our form:

import React from "react"
import { Formik } from "formik"
import * as yup from "yup"
import HandleError from "./HandleError"

function buyApples(applesAmount) {
  alert(`You have purchased ${applesAmount} apples`)
}

const appleFormValidation = yup.object().shape({
  applesAmount: yup.number().required().positive().integer(),
})

export default function ExampleFormikForm() {
  return (
    <Formik
      initialValues={{
        applesAmount: 0,
      }}
      validationSchema={appleFormValidation}
      onSubmit={values => {
        buyApples(values.applesAmount)
      }}
    >
      {({
        handleChange,
        handleBlur,
        values,
        handleSubmit,
        errors,
        touched,
      }) => (
        <form onSubmit={handleSubmit}>
          <HandleError name="applesAmount" />
          <label htmlFor="applesAmount">
            <span>Amount of apples:</span>
            <input
              name="applesAmount"
              type="number"
              onChange={handleChange}
              onBlur={handleBlur}
              value={values.applesAmount}
            />
          </label>
          <button type="submit">Buy apples</button>
        </form>
      )}
    </Formik>
  )
}

This will work in the same way as before, but now we do not need to pass down any props except from the name of the input we are displaying an error for.

How to use Formik hooks in react:

Let’s now take a look at using hooks in Formik to make slightly more advanced forms and ever create your own form component.

useField:

Let’s start by creating a custom input component, that can include the label and error handling, rather than just using a html input.

import { useField } from "formik"
import HandleError from "./HandleError"

export default function CustomFormInputExample({ name, label, ...props }) {
  const [field] = useField({ name, ...props })
  return (
    <>
      <HandleError name={name} />
      <label htmlFor={name}>
        <span>{label}</span>
        <input {...field} {...props} />
      </label>
    </>
  )
}

Now all we have to do is to plug this into our Formik form:

import React from "react"
import { Formik } from "formik"
import * as yup from "yup"
import CustomFormInputExample from "./CustomFormInputExample"

export default function ExampleFormikForm() {
  return (
    <Formik
      initialValues={{
        applesAmount: 0,
      }}
      validationSchema={appleFormValidation}
      onSubmit={values => {
        buyApples(values.applesAmount)
      }}
    >
      {({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <CustomFormInputExample
            name="applesAmount"
            label="Amount of apples:"
          />
          <button type="submit">Buy apples</button>
        </form>
      )}
    </Formik>
  )
}

Now as you can see, we have our form with our input components and everything should be working as it was.

But what if we want to have a slightly more complex input?

We need to create our own Formik form component using hooks.

For this, let’s say we want one of our inputs to add the input text into an array when the user clicks a button next to the input.

Let’s take a look at how we can go about doing that:

import { useField } from "formik"
import React, { useState } from "react"
import styles from "./MultipleEntryInputExample.module.css"

export default function MultipleEntryInputExample({ label, ...props }) {
  const [nextValue, setNextValue] = useState({ name: "", value: "" }) // this will control the internals of this component
  const [{ value: fieldValue }, , { setValue }] = useField(props) // this is where we store all our entries
  return (
    <div className={styles.container}>
      <h3>{label}</h3>
      <div className={styles.multiInputs}>
        <input
          label="Name"
          className={styles.input}
          name={`multi-name-${props.name}`}
          onChange={e => setNextValue(s => ({ ...s, name: e.target.value }))}
          value={nextValue.name}
        />
        <input
          label="Value"
          className={styles.input}
          name={`multi-value-${props.name}`}
          onChange={e => setNextValue(s => ({ ...s, value: e.target.value }))}
          value={nextValue.value}
        />
        <button
          className={styles.button}
          onClick={() => {
            if (!nextValue.name && !nextValue.value) {
              return
            }

            const next = {
              name: nextValue?.name || nextValue.value,
              value: nextValue?.value || nextValue.name,
            }

            setValue([...fieldValue, next]) // add new entry to exsiting array
            setNextValue({ name: "", value: "" }) // after adding to the array we reset the internal state
          }}
        >
          <span>+</span>
        </button>
      </div>
      <ul className={styles.list}>
        {fieldValue.map(({ name, value }) => (
          <li className={styles.multiInputs} key={value}>
            <input value={name} className={styles.inputAdded} disabled />
            <input value={value} className={styles.inputAdded} disabled />
            <button
              className={styles.button}
              onClick={() => {
                setValue(fieldValue?.filter(entry => entry.value !== value))
              }}
            >
              <span className={styles.minus}>-</span>
            </button>
          </li>
        ))}
      </ul>
    </div>
  )
}

Complex custom Formik component in react

As you can see, we have now made use of one of a Formik hook called useField to get and set the array in the Formik form.

The useField hook will take the name we give it that will relate to a form property, and then return us the value of the field and the setValue function to update the value in the form.

We also have a useState hook in our component to keep track of the name and value of the new entry that the user is typing into the input field.

When the user clicks on the button, we add the new entry to the form, and then clear the input state.

So the state here is controlling the input element and the form is controlling the array of data that is updated from our input.

This hook will probably be the one you use most often, but there is another hook that you may end up making use of called useFormikContext.

useFormikContext:

The useFormikContext hook gives you access to the form state/props that is passed into the children of the Formik component.

Just like how we used this earlier when we created our error handling component.

When to use Formik?

The last topic that we should cover here is: when to use Formik in React?

The answer to this will be largely circumstantial and personal to each use case and each developer.

With that being said, the short answer would be: 99% — 100% of the time. So if you are in doubt, go ahead and use Formik.

Given that it will be highly dependent on the project, and the form you are using and assuming that the alternatives are like we stated before (useState, useReducer, redux, writing your own version) I will generalise and say that for the most part, it is safe to assume that you should just use Formik.

There might be a few cases like if you have a very simple form with only one input and your project is not already using Formik then it might not be worth adding in Formik if this is the only place you would need to use it.

So to summarise when to use Formik in react, it is safe to assume that most of the time, if not always (when compared with the alternatives above), you should just go ahead and use it.

Summarising how to use Formik in React.

Formik is an amazing tool that saves a lot of time, effort and hassle when building forms in React.

Not to mention, it handles all the state, and getting, and setting of that state so it reduces the amount of code you have to write and maintain considerably.

It has built in validation that can be supplemented with the Yup library which makes life even easier.

Formik hooks means it is simple and easy to write custom form components and write forms with higher complexity as well.

Will

Introduction

Errors are a necessary part of all forms. Form requirements can range from very simple, to very complex. They range from simpler synchronous validations, to also including asynchronous validation. Formik has first class support for both synchronous and asynchronous validation. The basic form of this is the validate function. It can be a sync or async function.

Checking for Errors

The way that Formik handles errors is that the returned error structure should map an error message to same structure as your values. In our case we have a single level object with a firstName key. So if our value below is set like this, then our error should map accordingly.

{
  firstName: ''
}

{
  firstName: 'Erorr message',
}

It doesn’t matter how you check for errors in this function it just matters that the structure is returned, or if valid that an empty object is returned.

So here we check if firstName is empty and if it is we set an error message.

<Formik
  initialValues={{
    firstName: "",
  }}
  onSubmit={handleSubmit}
  validate={(values) => {
    let errors = {};
    if (!values.firstName) {
      errors.firstName = "Required";
    }
    return errors;
  }}
></Formik>

Controlling Error Rendering

Here we will control how an error gets rendered. The useField hook and Field both supply a meta object. The meta object is meta information about the specific field that’s being rendered.

So in our case it’s referencing meta information about our firstName field. A field might be in an erroneous state but we don’t want to bombard the user with the error message before they’ve even touched the field.

When a user interacts with a field, then triggers an onBlur the touched value is set to true. This allows us to now display an error message once we know it’s been touched and also that an error exists.

const MySpecialField = () => {
  const [field, meta] = useField("firstName");
  return (
    <div>
      <input {...field} className="border-2" />
      {meta.touched && meta.error && <div>{meta.error}</div>}
    </div>
  );
};

Formik Helper ErrorMessage

Generally the standard for displaying an error is that you need to have touched the field, and that an error exists. Formik ships with the ErrorMessage helper component.

import { ErrorMessage } from "formik";

You supply it with a name. When that fields contains an error and the field has been touched. Then it will display whatever the error text is. You can supply any component you’d like and it will pass the error text as a child.

<div>
  <Field name="firstName" className="border-2" />
  <ErrorMessage name="firstName" component="div" />
</div>

Submission

One thing to realize is that if the user submits all fields are set to the touched status. So even if the user didn’t trigger an onBlur on the firstName field. Then opted to click the Submit button, the error messages will pop up.

This is generally what you want but if this isn’t than you may need to handle how errors are displayed in your form and not use the ErrorMessage component.

Putting it all Together

import React from "react";
import { Formik, Form, Field, useField, ErrorMessage } from "formik";

const MySpecialField = () => {
  const [field, meta] = useField("firstName");
  return (
    <div>
      <input {...field} className="border-2" />
      {meta.touched && meta.error && <div>{meta.error}</div>}
    </div>
  );
};

function App() {
  const handleSubmit = (values) => {
    console.log(values);
  };

  return (
    <div className="App">
      <Formik
        initialValues={{
          firstName: "",
        }}
        onSubmit={handleSubmit}
        validate={(values) => {
          let errors = {};
          if (!values.firstName) {
            errors.firstName = "Required";
          }
          return errors;
        }}
      >
        {() => {
          return (
            <Form className="h-screen flex content-center flex-col justify-center">
              <div className="space-x-4 flex content-center justify-center">
                <div>
                  <Field name="firstName" className="border-2" />
                  <ErrorMessage name="firstName" component="div" />
                </div>
                <MySpecialField />
                <button
                  type="submit"
                  className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
                >
                  Submit
                </button>
              </div>
            </Form>
          );
        }}
      </Formik>
    </div>
  );
}

export default App;

Sep 1, 2018

Building forms has always been one of React’s weaknesses. The unidirectional data flow paradigm in React doesn’t mesh with the ephemeral-ness of HTML forms. To get around this, we’d need to leverage React’s local state and update that state with the value of the form whenever an input was changed. This made maintaining forms difficult, tedious, and unscalable.

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

We’ll use Formik to help cover business cases and how it can make building dynamic forms that are validated all on their own incredibly simple and easy. For this post, I have a git repo that will be split in multiple branches to cover each case for brevity and conciseness. You can clone it here.

Case #1: Initial form

Let’s write a basic form with Formik. No styles, just a multitude of different fields and a submit button. We can log out all the values we have. Let’s do that:

import React from "react";
import { Formik } from "formik";

const initialValues = {
    firstName: "",
    pet: "",
};

const App: React.SFC = () => (
    <>
        <h1>Working with Formik</h1>
        <Formik initialValues={initialValues}
            onSubmit={(values) => console.log(values)}
            render={({ handleSubmit, handleChange }) => (
                <form onSubmit={handleSubmit}>
                    <label htmlFor="firstName">
                        <div>First Name</div>
                        <input type="text" name="firstName"/>
                    </label>
                    <label htmlFor="pet">
                        <div>Pet</div>
                        <select name="pet" onChange={handleChange}>
                            <option>Dog</option>
                            <option>Cat</option>
                            <option>Other</option>
                        </select>
                    </label>
                    <button type="submit">Submit</button>
                </form>
            )}>
        </Formik>
    </>
);

export default App;

Here we have a really basic HTML form with helpers provided to us by Formik. However it’s pretty tedious to have to constantly add the name and onChange props to all our fields. Let’s make it so that we don’t need to do that, leveraging Formik’s Field component. It automatically wires up those props for you, taking in only the name as a prop.

import React from "react";
import { Formik, Field } from "formik";

const initialValues = {
    firstName: "",
    pet: "",
};

const App: React.SFC = () => (
    <>
        <h1>Working with Formik</h1>
        <Formik initialValues={initialValues}
            onSubmit={(values) => console.log(values)}
            render={({ handleSubmit }) => (
                <form onSubmit={handleSubmit}>
                    <label htmlFor="firstName">
                        <div>First Name</div>
                        <Field type="text" name="firstName"/>
                    </label>
                    <label htmlFor="pet">
                        <div>Pet</div>
                        <Field name="pet" component="select">
                            <option value="Dog">Dog</option>
                            <option value="Cat">Cat</option>
                            <option value="Other">Other</option>
                        </Field>
                    </label>
                    <button type="submit">Submit</button>
                </form>
            )}>
        </Formik>
    </>
);

export default App;

Aside: Here, we can use the <Form/> helper to replace our <form onSubmit={handleSubmit}/> tag, but for the sake of simplicity we’ll leave the code as is.

Note: The git repo will have the most up-to-date code in the app.tsx file, but each branch will contain files for code examples in this post to make it easier to reference.

Case #2: Form validation

Now that we have our form, let’s try to add some validation to it. Luckily for us, Formik provides a hook for that. Let’s check out the case/form-validation branch and continue on from there.

Let’s add some validation to our form and make entering your first name required.

<Formik initialValues={initialValues}
            onSubmit={(values) => console.log(values)}
            validate={(values) => {
                const errors = {
                    firstName: null,
                };
                if (!values.firstName) {
                    errors.firstName = "Required";
                }
                return errors;
            }}
            // render the rest of the component...

We can add validation on a per-field basis, mapped to the name of the input. The validate prop via Formik will fire this function every time the form is submitted. If there are errors, then it will return an errors object. If there are no fields inside of the errors object, then the form will be submitted.

Suppose we want to type our form values? With TypeScript we can create an interface for our form values to take advantage of static types!

interface FormValues {
    firstName: string;
    pet: string;
}

We’ll also import the FormikErrors type from the Formik package and type the rest of our component:

import React from "react";
import { Formik, Field, FormikErrors } from "formik";

export interface FormValues {
    firstName: string;
    pet: string;
}

const initialValues: FormValues = {
    firstName: "",
    pet: "",
};

const App: React.SFC = () => (
    <>
        <h1>Working with Formik</h1>
        <Formik initialValues={initialValues}
            onSubmit={(values: FormValues) => console.log(values)}
            validate={(values: FormValues) => {
                const errors: FormikErrors<FormValues> = {};
                if (!values.firstName) {
                    errors.firstName = "Required";
                }
                return errors;
            }}
            // render the form...

Presto! Now we have static types that make it easy for other developers to see what the form data will return.

However, suppose you have a lot of fields? It’d be tedious to write a custom validation function for every field. Let’s use Yup for our form validation.

Note: at this point in time, the code will be saved in another file called ValidatedForm.tsx and we’ll update our app.tsx file.

import { string, object } from "yup";

const App: React.SFC = () => (
    <>
        <h1>Working with Formik</h1>
        <Formik initialValues={initialValues}
            onSubmit={(values: FormValues) => console.log(values)}
            validationSchema={object().shape({
                firstName: string().required(),
            })}
            // render the form...

Here we got rid of our custom validate function, and used a custom schema via Yup. What if we want to return a specific error message? We can add a string as a param for all of yup’s validation:

firstName: string().required("Entering your first name is required.")

This string will be returned via Formik’s errors object if the schema fails. Let’s add some markup to display the error to the user:

// Formik component...
render={({ handleSubmit, errors, touched }) => (
    <form onSubmit={handleSubmit}>
        <label htmlFor="firstName">
            <div>First Name</div>
            <Field type="text" name="firstName"/>
            {
                touched.firstName && errors.firstName
                ? <div>{errors.firstName}</div>
                : null
            }
        </label>
        // the rest of the form...

As you can see, Formik gives us a lot of tools to work with. Run the application and try to submit an empty firstName field. We’ll see an error message pop up just underneath the field, and the form will not be submitted.

In summary, using Yup with Formik provides an easy experience to creating forms and validating them. This takes out most of the legwork when creating forms in React, allowing developers to focus on building real forms, not fighting form validation or form creation.

Next, we’ll create custom styled fields and spice up our form using styled-components! Check out the case/styled-fields branch.

Case #3: Custom styled fields

The idea behind having custom fields is that we can leverage the power of React to compartmentalize our responsibilities. Let’s make a custom React component that will render a Formik Field and also handle the rendering of any error messages.

// TextField.tsx

import React from "react";
import { Field, FieldProps } from "formik";
import { FormValues } from "./app";

interface Props {
    title: string;
    name: string;
}

type OwnProps = FieldProps<FormValues> & Props;
const TextField: React.SFC<OwnProps> = ({ name, title, field, form }) => (
    <label htmlFor={name}>
        <div>{title}</div>
        <Field type="text" name={name}/>
        {
            form.touched[name] && form.errors[name]
            ? <div>{form.errors[name]}</div>
            : null
        }
    </label>
);

export default TextField;

I exported the FormValues interface so we can use it here. Now we can specify a TextField in our form:

import React from "react";
import { FieldProps } from "formik";
import { FormValues } from "./app";

interface Props {
    title: string;
}

type OwnProps = FieldProps<FormValues> & Props;
const TextField: React.SFC<OwnProps> = ({ title, field, form }) => (
    <label htmlFor={field.name}>
        <div>{title}</div>
        <input type="text" {...field}/>
        {
            form.touched[field.name] && form.errors[field.name]
            ? <div>{form.errors[field.name]}</div>
            : null
        }
    </label>
);

export default TextField;

Then back in our form:

// App...
<Field name="firstName" render={(innerProps) => (
    <TextField {...innerProps} title="First Name"/>
)}/>
// render the rest of the form...

Now we have a reusable text field component that isn’t tightly coupled to only a single field. Now we can add multiple text fields to our form, while having each form field maintain its own validation and styles. Let’s go ahead and add those styles now:

import styled from "styled-components";

const Title = styled.h2`
    font-family: Arial, Helvetica, sans-serif;
    color: cornflowerblue;
    margin-bottom: 5px;
`;

const FormInput = styled.input`
    border-color: gray;
    border: 1px solid;
    padding: 5px 5px;
    margin-bottom: 5px;
`;

const InputError = styled.div`
    color: red;
    font-family: Arial, Helvetica, sans-serif;
    margin: 15px 0;
`;

Adding them to our component:

// TextField.tsx
const TextField: React.SFC<OwnProps> = ({ title, field, form }) => (
    <label htmlFor={field.name}>
        <Title>{title}</Title>
        <FormInput type="text" {...field}/>
        {
            form.touched[field.name] && form.errors[field.name]
            ? <InputError>{form.errors[field.name]}</InputError>
            : null
        }
    </label>
);

Now we have a snazzy looking text field that’s easy to maintain both field logic and styles all in one component.

Case #4: Array of Fields

By now you’ll be asking me: «But Scott, I just wanna map over these same fields and be done with my life and finally go outside.» Boy do I have something for you! Formik ships with a helper React component called <FieldArray/>. This is a helper component that handles an array of the same type of fields. Suppose we have not one person, but three people that we want to know their first names and their preferred pet:

First, we’ll need to update our initialValues:

const initialValues: FormValues = {
    people: [
        {
            firstName: "",
            pet: "",
        },
        {
            firstName: "",
            pet: "",
        },
        {
            firstName: "",
            pet: "",
        },
    ],
};

And we’ll have a type error, so we need to fix that:

export interface FormValues {
    people: Array<{
        firstName: string;
        pet: string;
    }>;
}

And update our Yup schema:

validationSchema={object().shape({
    people: array().of(object().shape({
        firstName: string().required("Entering a first name is required"),
    })),
})}

Then we have our new form, with an array of duplicated fields!

const App: React.SFC = () => (
    <>
        <h1>Working with Formik</h1>
        <Formik initialValues={initialValues}
            onSubmit={(values: FormValues) => console.log(values)}
            validationSchema={object().shape({
                people: array().of(object().shape({
                    firstName: string().required("Entering a first name is required"),
                })),
            })}
            render={({ handleSubmit, errors, touched, values }) => (
                <Form>
                    <FieldArray name="people"
                    render={(helpers) => (
                        <div>
                            {values.people && values.people.length > 0 ? (
                                values.people.map( (person, index) => (
                                    <React.Fragment key={index}>
                                        <Field name={`people.${index}.firstName`} render={(innerProps) => (
                                            <TextField {...innerProps} title="First Name"/>
                                        )}/>
                                        <label htmlFor="pet">
                                            <div>Pet</div>
                                            <Field name={`people.${index}.pet`} component="select">
                                                <option value="Dog">Dog</option>
                                                <option value="Cat">Cat</option>
                                                <option value="Other">Other</option>
                                            </Field>
                                        </label>
                                    </React.Fragment>
                                ))
                            ) : null}
                        <button type="submit">Submit</button>
                        </div>
                    )}/>
                </Form>
            )}>
        </Formik>
    </>
);

But when we submit it, we need to update our TextField component to recognize which error to render:

// TextField.tsx
const TextField: React.SFC<OwnProps> = ({ title, field, form, index }) => (
    <label htmlFor={field.name}>
        <Title>{title}</Title>
        <FormInput type="text" {...field}/>
        {
            form.errors.people !== undefined
            && form.errors.people[index] !== undefined
            && form.touched.people !== undefined
            && form.touched.people[index] !== undefined
            ? <InputError>{form.errors.people[index].firstName}</InputError>
            : null
        }
    </label>
);

This will now allow us to have an array of text fields that have per-field validation! Using Yup with Formik is immensely powerful, as we can specify a single schema and leverage that across all of our form fields.

Summary

Formik is a fantastic way to compartmentalize smaller form chunks into their own field-level components, while allowing the parent form to control the validation of each form input according to a schema. Adding forms this way makes it much easier to maintain React forms because the child fields only need to render the field and the error messages if there is one. Each child field maintains that singular responsibility, while the parent form maintains the validation, submission, and rendering of children. Suppose we want to update our fields to add some sort of analyitics? It’s trivial to do so, as we can update our TextField.tsx file.

As always, pull requests and comments are greatly appreciated: https://github.com/scottdj92/forms-with-formik.

Building forms with React involves setting up state as the container for user data and props as the means to control how state is updated using user input. Validation can be done in between user inputs, and an arbitrary submit function is executed on form submit.

Here is an example of a basic React form written without libraries and with minimal Bootstrap styling:

In the example below, we first initialize required state values in the constructor method. Since we have two required inputs — email and password — we initialize state for input values, input validity, and input errors:

constructor(props) {
  super(props);
  this.state = {
    formValues: {
      email: "",
      password: ""
    },
    formErrors: {
      email: "",
      password: ""
    },
    formValidity: {
      email: false,
      password: false
    },
    isSubmitting: false
  };
}

Next, we create the render method of the form with input values derived from state:

render() {
  const { formValues, formErrors, isSubmitting } = this.state;
  return (
    <div className="container">
      <div className="row mb-5">
        <div className="col-lg-12 text-center">
          <h1 className="mt-5">Login Form</h1>
        </div>
      </div>
      <div className="row">
        <div className="col-lg-12">
          <form onSubmit={this.handleSubmit}>
            <div className="form-group">
              <label>Email address</label>
              <input
                type="email"
                name="email"
                className={`form-control ${
                  formErrors.email ? "is-invalid" : ""
                }`}
                placeholder="Enter email"
                onChange={this.handleChange}
                value={formValues.email}
              />
              <div className="invalid-feedback">{formErrors.email}</div>
            </div>
            <div className="form-group">
              <label>Password</label>
              <input
                type="password"
                name="password"
                className={`form-control ${
                  formErrors.password ? "is-invalid" : ""
                }`}
                placeholder="Password"
                onChange={this.handleChange}
                value={formValues.password}
              />
              <div className="invalid-feedback">{formErrors.password}</div>
            </div>
            <button
              type="submit"
              className="btn btn-primary btn-block"
              disabled={isSubmitting}
            >
              {isSubmitting ? "Please wait..." : "Submit"}
            </button>
          </form>
        </div>
      </div>
    </div>
  );
}

Now we need to write the handleChange method to update the state with user inputs:

handleChange = ({ target }) => {
  const { formValues } = this.state;
  formValues[target.name] = target.value;
  this.setState({ formValues });
  this.handleValidation(target);
};

Anytime the state values are updated, we’ll run a validation method against user inputs. This is our handleValidation method:

handleValidation = target => {
  const { name, value } = target;
  const fieldValidationErrors = this.state.formErrors;
  const validity = this.state.formValidity;
  const isEmail = name === "email";
  const isPassword = name === "password";
  const emailTest = /^[^s@]+@[^s@]+.[^s@]{2,}$/i;
  validity[name] = value.length > 0;
  fieldValidationErrors[name] = validity[name]
    ? ""
    : `${name} is required and cannot be empty`;
  if (validity[name]) {
    if (isEmail) {
      validity[name] = emailTest.test(value);
      fieldValidationErrors[name] = validity[name]
        ? ""
        : `${name} should be a valid email address`;
    }
    if (isPassword) {
      validity[name] = value.length >= 3;
      fieldValidationErrors[name] = validity[name]
        ? ""
        : `${name} should be 3 characters minimum`;
    }
  }
  this.setState({
    formErrors: fieldValidationErrors,
    formValidity: validity
  });
};

The last part of this basic form is a handleSubmit method for the submission process. We need to check on formValidity values, and if there are any false values, run the validation method again without submitting the form.

handleSubmit = event => {
  event.preventDefault();
  this.setState({ isSubmitting: true });
  const { formValues, formValidity } = this.state;
  if (Object.values(formValidity).every(Boolean)) {
    alert("Form is validated! Submitting the form...");
    this.setState({ isSubmitting: false });
  } else {
    for (let key in formValues) {
      let target = {
        name: key,
        value: formValues[key]
      };
      this.handleValidation(target);
    }
    this.setState({ isSubmitting: false });
  }
};

Now the form is ready for use. React only provides the “view” layer for your application, and that means it provides only the basic necessities in making form components. component, state, and props are like puzzle blocks that you have to piece together to build a working form.

As you can see, it’s quite a lot of code for a form with only two text boxes. Imagine how many state values you need to keep track of in a form with 10 inputs or more. Yikes!

Yes, making forms with React is no fun; it’s very verbose and rigid. Building the form and creating validation method are boring tasks. In each form, you’d need to do the following, at a minimum:

  1. Set up state for form values, form errors, and form validity
  2. Handling user inputs and updating state
  3. Creating validation functions
  4. Handling submission

Building forms the natural “React” way requires you to write every part of the process from setting up states to form submission. I have done countless React forms, and I always find this part of building forms very boring and time-consuming. Fortunately, I’m not the only one feeling that way.

Enter Formik

Jared Palmer authored the Formik library out of frustration when building React forms. He needed a way to standardize the input components and the flow of form submission. Formik helps you to write the three most annoying parts of building a form:

  1. Getting values in and out of form state
  2. Validation and error messages
  3. Handling form submission

Here is the same form again, but this time using Formik:

This new form only uses four extra components from Formik library: <Formik />, <Form />, <Field />, and <ErrorMessage />. In order to unlock Formik’s power, you can wrap your form inside the <Formik /> component:

<Formik>
  <Form>
    {/* the rest of the code here */}
  </Form>
</Formik>

Let’s see how Formik makes building forms easier compared to React’s natural way.

Getting values in and out of form state

Formik will set up state internally for storing user inputs through its initialValues prop, so you don’t need to initialize state from constructor anymore.

In order to get values in and out of Formik internal state, you can use the <Field /> component to replace the regular HTML <input /> component. This component will do the magic of keeping Formik state and input value in sync, so you don’t have to pass value and onChange props into the <Field /> component:

<Formik
  initialValues={{ email: "", password: "" }}
  onSubmit={({ setSubmitting }) => {
    alert("Form is validated! Submitting the form...");
    setSubmitting(false);
  }}
>
  {() => (
    <Form>
      <div className="form-group">
        <label htmlFor="email">Email</label>
        <Field
          type="email"
          name="email"
          className="form-control"
        />
      </div>
      <div className="form-group">
        <label htmlFor="password">Password</label>
        <Field
          type="password"
          name="password"
          className="form-control"
        />
      </div>
    </Form>
  )}
</Formik>

With Formik, there’s no need to initialize state in constructor and create your own handleChange method anymore. It’s all taken care of.

Validation and error messages

Validation in Formik is executed automatically during specific events. All common events like after user input, on focus change, and on submit are covered, and you don’t have to worry about them. All you need to do is pass a function into Formik’s validate prop.

Compare this code between Formik validation and vanilla React validation:

// Formik validation code. Take values from Formik
validate={values => {
  let errors = {};
  if (values.email === "") {
    errors.email = "Email is required";
  } else if (!emailTest.test(values.email)) {
    errors.email = "Invalid email address format";
  }
  if (values.password === "") {
    errors.password = "Password is required";
  } else if (values.password.length < 3) {
    errors.password = "Password must be 3 characters at minimum";
  }
  return errors;
}}

// Vanilla React validation code. Take values given by handleChange
handleValidation = target => {
  const { name, value } = target;
  const fieldValidationErrors = this.state.formErrors;
  const validity = this.state.formValidity;
  const isEmail = name === "email";
  const isPassword = name === "password";
  const emailTest = /^[^s@]+@[^s@]+.[^s@]{2,}$/i;
  validity[name] = value.length > 0;
  fieldValidationErrors[name] = validity[name]
    ? ""
    : `${name} is required and cannot be empty`;
  if (validity[name]) {
    if (isEmail) {
      validity[name] = emailTest.test(value);
      fieldValidationErrors[name] = validity[name]
        ? ""
        : `${name} should be a valid email address`;
    }
    if (isPassword) {
      validity[name] = value.length >= 3;
      fieldValidationErrors[name] = validity[name]
        ? ""
        : `${name} should be 3 characters minimum`;
    }
  }
  this.setState({
    formErrors: fieldValidationErrors,
    formValidity: validity
  });
};

With validation in place, now you need to output error messages. Formik’s <ErrorMessage /> component will automatically display error message for the <Field /> component with the given name. You can adjust what HTML tag will be displayed through the component prop. Since this example form is using Bootstrap’s style, you will have to add a className prop as well:

// Formik error message output
<Field
  type="email"
  name="email"
  className={`form-control ${
    touched.email && errors.email ? "is-invalid" : ""
  }`}
/>
<ErrorMessage
  component="div"
  name="email"
  className="invalid-feedback"
/>

// Vanilla React error message output
<input
  type="email"
  name="email"
  className={`form-control ${
    formErrors.email ? "is-invalid" : ""
  }`}
  placeholder="Enter email"
  onChange={this.handleChange}
  value={formValues.email}
/>
<div className="invalid-feedback">{formErrors.email}</div>

The code for error message is actually about the same, but there’s a lot less code in Formik’s validation than in vanilla React. Way to go, Formik!

Even easier validation with Yup

Although you can already feel the benefit of using Formik in validation process, you can make it even easier by using an object schema validator.

An object schema validator is simply a library that allows you to define the blueprint of a JavaScript object and ensure that the object values match that blueprint through the validation process. This is particularly useful in validating form data since it’s actually an object kept inside Formik’s values prop.

Now one such library is Yup, and Formik’s author loves Yup so much that he included a special prop that connects Yup with Formik called validationSchema. This prop will automatically transform Yup’s validation errors into a pretty object whose keys match values and touched.

Here is an example of Formik using Yup as its validation schema. Notice how the validate prop is removed from the <Formik /> component:

With Yup’s object schema validator on place, you don’t have to manually write if conditions anymore. You can learn more about Yup and what kind of validation it can do by visiting its GitHub repo.

Form submission process

Formik’s <Form /> component will automatically run your validation method and cancel the submission process if there are any errors. While you have to include the onSubmit prop to a regular <form /> element, Formik’s <Form /> wrapper will run the onSubmit prop function you passed into the <Formik /> component:

// Formik's submit code. Won't be executed if there are any errors.
onSubmit={({ setSubmitting }) => {
  alert("Form is validated!");
  setSubmitting(false);
}}

// Vanilla React submit code. Check on validity state then run validation manually.
handleSubmit = event => {
  event.preventDefault();
  this.setState({ isSubmitting: true });
  const { formValues, formValidity } = this.state;
  if (Object.values(formValidity).every(Boolean)) {
    alert("Form is validated!");
    this.setState({ isSubmitting: false });
  } else {
    for (let key in formValues) {
      let target = {
        name: key,
        value: formValues[key]
      };
      this.handleValidation(target);
    }
    this.setState({ isSubmitting: false });
  }
};

Formik requires only four lines of code for submission at minimum, and you don’t need to keep track of the validity of form inputs. That’s pretty neat!

But what about redux-form?

Sure, redux-form works great, but then you’d need to use Redux in the first place. What if you’re using MobX? What if a new, better library comes up in the future and you want to replace Redux with that? On top of all that, does your React form actually affects the flow of your entire application in some way?

Think about it: Does the value of the username textbox somehow matter to your application globally? If not, then it’s really not necessary to track its value using Redux. Even the prophet Dan Abramov said the same thing.

Another problem with redux-form is that you are storing form input values into Redux store. This means your application will call on Redux’s reducer on every keystroke to update the value of just one textbox. Not a good idea.

I love writing forms the “Formik” way, but if you prefer redux-form, then that’s fine, too. 😉

Conclusion

Building forms is one of those things that React isn’t good at. Luckily, React has a community of developers that help each other and make the process of writing code easier.

Formik is definitely one of those open source libraries that’s a must-have if you are writing many forms in your React application. It really speeds up your development process and reduces boilerplate code by abstracting away parts of your form through components like <Field /> and <Form />.

While a vanilla React form requires you to specify your own state values and methods, you can simply pass props to the <Formik /> component to do the same things: handle user inputs, validate inputs, and form submission.

If you’d like to learn more about Formik, head over to the documentation or watch the presentation below by its creator.

React Distilled 2.0 is released

If you’d like to learn more about React and how you can use it to build a complete web application from scratch, I’m offering a 28% off my book React Distilled to celebrate its release (from $49 to $34). 

It includes new chapters on React Context API and React Hooks, and it shows how you can create React app using nothing but React and Firestore.

Go grab it now so you can be a React Genius today!

1_VEWXDp2iflV30wSyBBfEPA.png


Originally published at https://blog.logrocket.com on June 28, 2019.

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Formhack сервер недоступен как исправить
  • Former volume not mounted utorrent как исправить
  • Formatter silicon power error code 121
  • Formatter internal ram or rom error
  • Format is not supported or source is unavailable как исправить

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии