Build forms using React, the easy way ( with Typescript )
Tags: react, javascript, form, typescript
Published: 2021-02-01 10:04:47


*"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: 

```bash
npx create-react-app react-hook-form
```

### 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"`.

```tsx
import { useState } from "react";
```

### 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.

```tsx
import { useState } from "react";

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

}
```
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.

 ```tsx
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 });
    };

}
```

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.

```tsx
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
    };
```

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.



```tsx
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,
    };
```
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.

```tsx
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;
```

**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](https://react-hook-form.com/) 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.