February 8, 2023

React Form Using Formik's useFormik() Hook

In the post React Form Creation and Validation Using React Hooks we have seen how to create a form using React and how to do validation. As you can notice the whole process of creating form in React is quite verbose if you do everything on your own.

You need to have a lot of repeated code for-

  1. Managing form fields states.
  2. Validation and when to show/not show error messages.
  3. Handling form submission

Formik, a third-party lightweight React form library addresses the above mentioned issues. Formik standardizes the creation of from fields and the form submission process.

Formik installation

You can install Formik with Yarn or NPM

With Yarn

yarn add formik

With npm

npm install formik -save

Formik React form example

In this example we'll create a form with 5 fields- First Name, Last Name, Join Date, Email and Employment Type. This example uses useFormik() which is a custom React hook.

Though using useFormik() hook is quite verbose and you can create a form using Formik in a more readable way using <Formik> component and using Yup for form validation. Check the post here- React Form + Formik + Yup Validation Example

Using useFormik() hook you can define the form fields, how to validate and form submission process at the same place.

const formik = useFormik({
  initialValues: {
    firstName: '',
    lastName: '',
    joinDate: '',
    email: '',
    employmentType: 'permanent'
  },
  validate,
  onSubmit: values => {
    alert(JSON.stringify(values, null, 2));
  },
});

As you can see form's initial values, a submission function (inSubmit) and a validation function (validate) is passed to the useFormik() hook.

Event handling

useFormik() hook returns form state and helper methods which can be used out of the box. Some of the helper methods you will use in your form are-

  • handleSubmit: A submission handler
  • handleChange: A change handler to pass to each <input>, <select>, or <textarea>
  • handleBlur: To keep track of which fields have been visited, stores this information in an object called touched which can store boolean values true or false
  • values: Our form’s current values

That's how you'll use handleSubmit

<form onSubmit={formik.handleSubmit}>
Using handleChange, handleBlur and value.
<input 
  type="text" 
  id="firstName" 
  name="firstName"
  onChange={formik.handleChange} 
  onBlur={formik.handleBlur}
  value={formik.values.firstName}>
</input>

With that Formik will take care of managing form fields state and event handling. You don't need to write custom event handlers for every single input.

Validation and error messages

Formik also keeps track of validation and error messages. You can specify a custom validation function and pass it as validate to the useFormik() hook. If an error exists, this custom validation function should produce an error object with properties matching the form values/initialValues.

You would generally show error once you come out of a form field. For that Formik has handleBlur() event handler to keep track of which fields have been visited.

That's how you'll write custom validate function.

const validate = values => {
    const errors = {};
    if (!values.firstName) {
        errors.firstName = 'Required';
    } else if (values.firstName.length > 10) {
        errors.firstName = 'Must be 10 characters or less';
    }
    // Validation for other fields

That's how you'll check if field visited and if field has error to display error message.

{formik.touched.firstName && formik.errors.firstName ? <div>{formik.errors.firstName}</div> : null}

Here is the full React form example created using useFormik() hook of Formik.

import { useFormik } from "formik"
import './form.css';
const validate = values => {
    const errors = {};
    if (!values.firstName) {
        errors.firstName = 'Required';
    } else if (values.firstName.length > 10) {
        errors.firstName = 'Must be 10 characters or less';
    }
    if (!values.lastName) {
        errors.lastName = 'Required';
    } else if (values.lastName.length > 15) {
        errors.lastName = 'Must be 15 characters or less';
    }
    if (!values.joinDate) {
        errors.joinDate = 'Required';
    }
    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;
}
const EmployeeForm = () => {
    const formik = useFormik({
        initialValues: {
            firstName: '',
            lastName: '',
            joinDate: '',
            email: '',
            employmentType: 'permanent'
        },
        validate,
        onSubmit: values => {
            console.log(JSON.stringify(values, null, 2));
        },
    });
    return (
        <form onSubmit={formik.handleSubmit}>
            <div>
            <label htmlFor="firstName">First Name</label>
            <input type="text" id="firstName" name="firstName"
                onChange={formik.handleChange} onBlur={formik.handleBlur}
                value={formik.values.firstName}></input>
            {formik.touched.firstName && formik.errors.firstName ? <div className="error">{formik.errors.firstName}</div> : null}
            </div>
            <div>
            <label htmlFor="lastName">Last Name</label>
            <input type="text" id="lastName" name="lastName"
                onChange={formik.handleChange} onBlur={formik.handleBlur}
                value={formik.values.lastName}></input>
             {formik.touched.lastName && formik.errors.lastName ? <div className="error">{formik.errors.lastName}</div> : null}        
            </div>
            <div>
            <label htmlFor="joinDate">Join Date</label>
            <input type="date" id="joinDate" name="joinDate"
                onChange={formik.handleChange} onBlur={formik.handleBlur}
                value={formik.values.joinDate}></input>      
             {formik.touched.joinDate && formik.errors.joinDate ? <div className="error">{formik.errors.joinDate}</div> : null}      
            </div>
            <div>
            <label htmlFor="email">Email</label>
            <input type="email" id="email" name="email"
                onChange={formik.handleChange} onBlur={formik.handleBlur}
                value={formik.values.email}></input>
            {formik.touched.email && formik.errors.email ? <div className="error">{formik.errors.email}</div> : null}    
            </div>
            <div>
            <label htmlFor="employmentType">Employment Type:</label>
                <select id="employmentType" name="employmentType" 
                  value={formik.values.employmentType} onChange={formik.handleChange} 
                  onBlur={formik.onBlur}>
                    <option value="permanent">Permanent</option>
                    <option value="contract">Contract</option>
                </select>
            </div>
            <button type="submit" disabled={!(formik.dirty && formik.isValid)}>Submit</button>
        </form>
    )
}
export default EmployeeForm;

To keep focus on the form styling is not used, only styling is for error message.

form.css

.error {
  color: red;
}

Some important points to note here-

  1. In the useFormik() hook, initialValues for the form fields are passed, all but one as empty fields, for the employmentType which is a drop-down list pre-selected option is there.
  2. A custom validation function is written to validate the form fields and create error messages. Reference for that function is also passed to the hook.
  3. onSubmit function just logs the form values.
  4. With form fields the same event handler functions (handleChange, handleBlur) are used, which is one of the benefit of using Formik library.
  5. Form values are passed to the validate function and the validation based on the requirement is done for the fields there. Error messages for the fields are also created.
  6. Formik keeps track of the visited fields and based on whether the field is touched or not and if it fails validation, error message for the field is displayed.

That's all for the topic React Form Using Formik's useFormik() Hook. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment