Mastering Form Management with React useForm Plugin Library

·

6 min read

Table of contents

No heading

No headings in the article.

Introduction:

Managing forms in web applications can be a complex task, especially when dealing with validation rules, error handling, and form submissions. The React useForm plugin library offers a powerful and elegant solution to simplify form management in React applications. In this tutorial, we will walk through a comprehensive example of using the useForm library to handle form lifecycle events, such as validations, rules, and form submissions. By the end of this tutorial, you will have a solid understanding of how to use the React useForm plugin library to efficiently manage forms in your React applications.

Prerequisites:

  • Basic understanding of JavaScript and React

  • Familiarity with React functional components and hooks

  • Knowledge of form validation and handling in web applications

Getting Started:

To begin, let's install the React useForm plugin library by running the following command:

npm install react-hook-form

Now that we have the library installed, let's dive into a practical example of using the useForm library to manage a form lifecycle.

Example: Create and Update Client Action Items

In this example, we will create a form to create and update client action items. The form will include fields for title, category, description, and target_date. We will use the useForm library to manage form state, handle validations, and submit the form data to an API.

Full Code Example;

import { useForm } from 'react-hook-form';

export default function CreateClientActionItem({item}) {
    const {
        register,
        handleSubmit,
        formState: {
            errors
        }
    } = useForm({
        defaultValues: {
            title: item?.title ?? '',
            category: item?.category ?? '',
            description: item?.description ?? '',
            target_date: item?.target_date ?? '',
        }
    });

    const onSubmit = (data) => {
        let url = '/test/store-url';
        axios.post(url, data).then((res) => {
            if (res.data.status === 'success') {
                alert("Success");
            } else {
                alert("Error");
            }
        }).catch((err) => {
            notifyError("Unknown error occurred while creating action item")
        });
    }

    const onUpdate = (data) => {
        let url = '/test/update-url';
        axios.put(url, data).then((res) => {
            if (res.data.status === 'success') {
                alert("Success");
            } else {
                alert("Error");
            }
        }).catch((err) => {
            notifyError("Unknown error occurred while updating action item")
        });
    }

    const onError = (data) => {
        console.log(data);
    }

    return <form onSubmit={handleSubmit((item == null) ? onSubmit : onUpdate, onError)}>
            <div
                className="flex flex-col gap-[24px] rounded-[5px] w-full">
                <div className="flex flex-col gap-[16px]">
                    <div className="flex flex-col gap-[16px]">
                        <label htmlFor="title">Title</label>
                        <input type="text" {...register('title', {
                            required: "Title is required",
                            minLength: {
                                value: 2,
                                message: "Title must be at least 2 characters"
                            },
                            maxLength: {
                                value: 50,
                                message: 'Title can be maximum 50 characters'
                            }
                        })} name="title" id="title"
                               className="w-full p-[10px] border border-gray-500 rounded-[5px] pl-10 pr-12 f-16-400-g-sans"
                               placeholder="Title"/>
                        {errors.title ?
                            <p className="text-red-500">{errors.title.message}</p> : null}
                    </div>
                    <div className="flex flex-col gap-[16px]">
                        <label htmlFor="category">Category (optional)</label>
                        <input type="text" {...register('category', {
                            minLength: {
                                value: 2,
                                message: "Category must be at least 2 characters"
                            },
                            maxLength: {
                                value: 50,
                                message: 'Category can be maximum 50 characters'
                            }
                        })} name="category" id="category"
                               className="w-full p-[10px] border border-gray-500 rounded-[5px] pl-10 pr-12 f-16-400-g-sans"
                               placeholder="Category"/>
                        {errors.category ?
                            <p className="text-red-500">{errors.category.message}</p> : null}
                    </div>
                    <div className="flex flex-col gap-[16px]">
                        <label htmlFor="description">Description</label>
                        <textarea {...register('description', {
                            required: "Description is required",
                            minLength: {
                                value: 2,
                                message: "Description must be at least 2 characters"
                            },
                        })} name="description" id="description"
                                  className="w-full p-[10px] border border-gray-500 rounded-[5px] pl-10 pr-12 f-16-400-g-sans"
                                  placeholder="Description"/>
                        {errors.description ?
                            <p className="text-red-500">{errors.description.message}</p> : null}
                    </div>
                    <div className="flex flex-col gap-[16px]">
                        <label htmlFor="target_date">Target date (optional)</label>
                        <input type="date" {...register('target_date')} name="target_date"
                               id="target_date"
                               className="w-full p-[10px] border border-gray-500 rounded-[5px] pl-10 pr-12 f-16-400-g-sans"
                               placeholder="Description"/>
                    </div>
                </div>
                <div className="flex flex-row items-center gap-[40px]">
                    <button type="submit" className="primary-button bg-defaultPrimary">{!item ? 'Create action item' : 'Update action item'}</button>
                    <h1 onClick={closeHandler} className="text-defaultPrimary cursor-pointer">Cancel</h1>
                </div>
            </div>
        </form>
}

Code Explanation:

1. Import useForm:

Start by importing the useForm hook from the library:

import { useForm } from 'react-hook-form';

2. Initialize useForm:

In the CreateClientActionItem functional component, initialize the useForm hook and destructure its returned properties:

const {
    register,
    handleSubmit,
    formState: {
        errors
    }
} = useForm({
    defaultValues: {
        title: item?.title ?? '',
        category: item?.category ?? '',
        description: item?.description ?? '',
        target_date: item?.target_date ?? '',
    }
});

The defaultValues option allows you to set initial values for the form fields.

In this example, we use the optional chaining operator “?” and the nullish coalescing operator “??” to set the default values to empty strings if item is not provided.

3. Register form fields:

To register form fields with the useForm hook, use the register function provided by the hook. The register function takes two arguments: the field name and an optional configuration object. The configuration object can include validation rules, such as required, minLength, and maxLength. If a validation rule fails, an error message is provided and stored in the errors object.

For example, registering the title field with validation rules:

<input type="text" {...register('title', {
    required: "Title is required",
    minLength: {
        value: 2,
        message: "Title must be at least 2 characters"
    },
    maxLength: {
        value: 50,
        message: 'Title can be maximum 50 characters'
    }
})} ... />

Similarly, register the category, description, and target_date fields with appropriate validation rules.

4. Handle form submission:

The handleSubmit function is used to manage form submissions. It takes two arguments: a function to be executed on a successful submission and a function to be executed if there are any errors.

In our example, we use the onSubmit function for creating a new action item and the onUpdate function for updating an existing one:

<form onSubmit={handleSubmit((item == null) ? onSubmit : onUpdate, onError)}>

The ternary operator checks if item is null, which indicates a new action item should be created. If item is not null, it means we are updating an existing action item.

- Display error messages:

If there are any validation errors, they are stored in the errors object. To display error messages, check if an error exists for a specific form field and render the error message accordingly:

{errors.title ?
    <p className="text-red-500">{errors.title.message}</p> : null}

In this example, if there is an error for the title field, the error message is displayed in a paragraph element with a text-red-500 class. Similarly, error messages are displayed for other fields such as category and description.

- Form submission logic:

In the onSubmit and onUpdate functions, we use the axios library to send HTTP requests to the API for creating or updating an action item.

The response is checked for the status property to determine if the request was successful.

If successful, we display a success notification, update the item, and close the form. If unsuccessful, we display an error notification:

axios.post(url, data).then((res) => {
    if (res.data.status === 'success') {
        alert('success');
    } else {
        alert('error');
    }
}).catch((err) => {
    notifyError("Unknown error occurred while creating action item")
});

In the onError function, we log the error data to the console. In a real-world scenario, you might want to handle the errors differently, such as by displaying a user-friendly error message.

Conclusion:

In this tutorial, we have explored how to use the React useForm plugin library to handle form lifecycle events, including validations, rules, and form submissions.

By using this powerful library, you can streamline form management in your React applications and build robust, user-friendly forms with ease.

The provided code example demonstrates a practical use case that can be adapted to suit your specific requirements. With this knowledge, you can now confidently tackle form management challenges in your React projects.

Happy coding!

Did you find this article valuable?

Support Building Portfolio Apps by becoming a sponsor. Any amount is appreciated!