DEV Community

Cover image for Build forms using React, the easy way ( with Typescript )
Karan Hejmadi
Karan Hejmadi

Posted on • Updated on

Build forms using React, the easy way ( with Typescript )

"forms, ughh.. I don't like it"

We all have been there. You might have tried several libraries like formik or redux forms to ease out your pain. Although decent solutions, you often get plenty of boilerplate code, which makes your code look less pretty and difficult to deal with for beginners. If you don't need the robustness of those libraries you can build your own custom form hook within a few lines of code.

This tutorial would require you to have some knowledge of modern react with hooks.

Step 1 : Initialize an empty create-react-app

Open the terminal and type:

npx create-react-app react-hook-form
Enter fullscreen mode Exit fullscreen mode

Step 2: Open the project in your code editor.

Go to the src directory and create a new directory with the name hooks. Inside the hooks directory create a useForm.ts file (useForm.js if you're using javascript)

Step 3: Importing dependencies

For the custom hook, we will import useState hook from "react".

import { useState } from "react";
Enter fullscreen mode Exit fullscreen mode

Step 4: Defining the functional component.

Create a functional component named useForm and define the initial state for the form. Don't forget to add the export keyword.

import { useState } from "react";

// useForm functional component
export const useForm = (callback: any, initialState = {}) => {
    const [values, setValues] = useState(initialState);

}
Enter fullscreen mode Exit fullscreen mode

Here, initialState will store the various values a form can have i.e. email, password, etc. which will be passed on from the component that uses this hook. callback is the function that will be executed when the user submits the form.

Step 5: onChange function

onChange function is used to handle change events whenever a user types something in the input field.

import { useState } from "react";

// useForm functional componen
export const useForm = (callback: any, initialState = {}) => {
    const [values, setValues] = useState(initialState);

    // onChange
    const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        setValues({ ...values, [event.target.name]: 
    event.target.value });
    };

}
Enter fullscreen mode Exit fullscreen mode

This function sets the target value of the event that is passed to the target name. Suppose you gave an input element a name as "email", the value that is entered in the email field will be set to the email attribute in the initialState. This requires the initialState to have the attributes with the same name as the name specified in the input field.

Step 6: onSubmit function

onSubmit() executes the callback() function that was passed on when the user clicked the submit button.

import { useState } from "react";

// useForm functional componen
export const useForm = (callback: any, initialState = {}) => {
    const [values, setValues] = useState(initialState);

    // onChange
    const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        setValues({ ...values, [event.target.name]: event.target.value });
    };

}

    // onSubmit
    const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
        event.preventDefault();
        await callback(); // triggering the callback
    };
Enter fullscreen mode Exit fullscreen mode

Your callback() would usually be an asynchronous function like submitting login data to the database, so we use the await keyword and define the onSubmit as an async function.

Step 7: Returning the hook's functions and data.

import { useState } from "react";

// useForm functional componen
export const useForm = (callback: any, initialState = {}) => {
    const [values, setValues] = useState(initialState);

    // onChange
    const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        setValues({ ...values, [event.target.name]: event.target.value });
    };

}

    // onSubmit
    const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
        event.preventDefault();
        await callback(); // triggering the callback
    };

    // return values
    return {
        onChange,
        onSubmit,
        values,
    };
Enter fullscreen mode Exit fullscreen mode

We return the onChange, onSubmit and values from this hook to the components that use this hook.

Step 8: Usage

Create a login.tsx file (login.jsx for javascript) in the src directory.
Add the following code.

import React, { useState } from "react";

import { useForm } from "./useForm";

function Login() {
    // defining the initial state for the form
    const initialState = {
        email: "",
        password: "",
    };

    // getting the event handlers from our custom hook
    const { onChange, onSubmit, values } = useForm(
        loginUserCallback,
        initialState
    );

    // a submit function that will execute upon form submission
    async function loginUserCallback() {
        // send "values" to database
    }

    return (
        // don't mind this ugly form :P
        <form onSubmit={onSubmit}>
        <div>
            <input
                name='email'
                id='email'
                type='email'
                placeholder='Email'
                onChange={onChange}
                required
                />

            <input
                name='password'
                id='password'
                type='password'
                placeholder='Password'
                onChange={onChange}
                required
                />
            <button type='submit'>Login</button>
        </div>
        </form>
    );
}

export default Login;
Enter fullscreen mode Exit fullscreen mode

DONE! No bulky form components, add more event handlers to your custom hook to make it more robust. Easy and simple.

There is an npm package called react-hook-form which is gaining popularity. This tutorial is a basic insight into that package. Try adding more features like form validation to this custom hook of yours and make form building using react a better place :)

Thank you for reading! Any feedback/questions would be appreciated.

Top comments (5)

Collapse
 
ppulwey profile image
Patrick Pulwey • Edited

Thank you this is nice. To have full TypeScript support in your Form use this hook declaration:

export const useForm = <T,>(callback: () => Promise<any>, initalState: T)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
brian1150 profile image
Brian-1150

Great post! Thank you for sharing!

Collapse
 
pads profile image
Juan Carlos Padillo

This is good. Thank you.

Collapse
 
sbussing profile image
Stephan Bussing

Thank you for this. Well explained and, for a beginner, easy to follow.

Collapse
 
ajagelund profile image
Andreas Jagelund

Love it!
Will be a great start for my new form.