The Power of Custom React Hooks: Building a Form with Validations
Tags: react, webdev, beginners, tutorial
Published: 2023-01-19 05:12:40

As I've been self studying React.js and recently learned about Custom Hooks, I decided it was time for a post about it!

In this example, I'll be showing step-by-step how to create some simple form validation, with error handling using Custom Hooks.

---

I will be working with three files. `App.js`, `Form.js` and `input-hook.js`

## Setup

**Project Structure:**
📦src
 ┣ 📂components
 ┃ ┣ 📜**Form.js**
 ┣ 📂hooks
 ┃ ┣ 📜**input-hook.js**
 ┣ 📜App.js
 ┣ 📜index.css
 ┗ 📜index.js

---



## `App.js`

Here we'll just render the form, nothing else.

```javascript
import Form from "./components/Form";

function App() {
  return (
    <div className="app">
      <Form />
    </div>
  );
}


export default App;
``` 

---

## `input-hook.js`
```javascript
import React, { useState } from "react";

const useInput = (validateValue) => {
  const [inputValue, setInputValue] = useState("");
  const [inputTouched, setInputTouched] = useState(false);

  const isValid = validateValue(inputValue);
  const hasError = !isValid && inputTouched;

  const inputChangeHandler = (e) => {
    setInputValue(e.target.value);
  };

  const inputBlurHandler = (e) => {
    setInputTouched(true);
  };

  const reset = () => {
    setInputValue("");
    setInputTouched(false)
  };

  return {
    value: inputValue,
    isValid,
    hasError,
    inputChangeHandler,
    inputBlurHandler,
    reset,
  };
};

export default useInput;
```
First, we import useState, as we will need it in our custom hook.

The `useInput` custom component starts by using the `useState` hook to create three state variables: `value`, `isInputValid`, and `hasError`. We also included a argument `validationFn`, which is where we will pass the logic for how to validate the field, from our `Form.js.`

`value`
- Keep track of what the user is typing
`isInputValid`
- Checks validity of whatever is typed
`hasError`
- A Boolean tracking both if the user started typing, and if what they typed is valid or not.


The first one `inputChangeHandler` is passed to the input's `onChange` prop. It updates the value state by checking the input's value.

The second one `inputBlurHandler` is passed to the input's `onBlur` prop. The handler checks if the user started typing or not.

The third one `reset` is the function we use to reset the form to it's initial state.

Finally, we return all the properties we need to use in our `Form.js` as an object.

## `Form.js`

**Form Structure**
```javascript
  return (
    <form onSubmit={submitHandler}>
      <div className="control-group">
        <div className={nameInputClasses}>
          <label htmlFor="name">First Name</label>
          <input
            value={nameInputValue}
            onBlur={nameBlurHandler}
            onChange={nameChangeHandler}
            type="text"
            id="name"
          />
          {nameHasError && <p className="error-text">Enter a name.</p>}
        </div>
        <div className={lastNameInputClasses}>
          <label htmlFor="lastname">Last Name</label>
          <input
            value={lastNameInputValue}
            onChange={lastNameChangeHandler}
            onBlur={lastNameBlurHandler}
            type="text"
            id="lastname"
          />
          {lastNameHasError && <p className="error-text">Enter a last name.</p>}
        </div>
      </div>
      <div className={emailInputClasses}>
        <label htmlFor="email">E-Mail Address</label>
        <input
          value={emailInputValue}
          onChange={emailChangeHandler}
          onBlur={emailBlurHandler}
          type="text"
          id="email"
        />
        {emailHasError && <p className="error-text">Enter a real email.</p>}
      </div>
      <button type="submit" disabled={!formIsValid}>
        Submit
      </button>
    </form>
  );
};

export default Form;
```

In this component, there will be a lot going on. I'm going to break it down piece by piece.

First, we import the necessary dependencies: `useInput` hook, `React`, and `useEffect` and `useState`.

```javascript
import useInput from "../hooks/input-hook";
import React, { useEffect, useState } from "react";
```

We then use the `useState` hook to create a state variable `formIsValid` which we set to false initially. It is used to check if our form is ready to be submitted or not.

```javascript
const [formIsValid, setFormIsValid] = useState(false);
````

Next we are using our `useInput` Custom Hook to create three new objects. One which checks `name`, `lastName` and lastly `email`.

In our `useInput`'s argument, we pass a anonymous function. In this situation, we use that as an opportunity to check the input.

For the name fields, I make sure it's not empty, and for the email field I use RegEx to make sure it's a valid email format(it's not perfect, but works).

````javascript
  const {
    value: nameInputValue,
    isInputValid: isNameValid,
    hasError: nameHasError,
    inputChangeHandler: nameChangeHandler,
    inputBlurHandler: nameBlurHandler,
    reset: nameReset,
  } = useInput((name) => name.trim() !== "");

  const {
    value: lastNameInputValue,
    isInputValid: isLastNameValid,
    hasError: lastNameHasError,
    inputChangeHandler: lastNameChangeHandler,
    inputBlurHandler: lastNameBlurHandler,
    reset: lastNameReset,
  } = useInput((lastName) => lastName.trim() !== "");

  const {
    value: emailInputValue,
    isInputValid: isEmailValid,
    hasError: emailHasError,
    inputChangeHandler: emailChangeHandler,
    inputBlurHandler: emailBlurHandler,
    reset: emailReset,
  } = useInput((email) =>
    email.trim().match(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/)
  );
```

Then, we use `useEffect` to check if the inputs have a value, and if so set the form validity to true.

```javascript
  useEffect(() => {
    if (nameInputValue && lastNameInputValue && emailInputValue) {
      setFormIsValid(true);
    } else {
      setFormIsValid(false);
    }
  }, [nameInputValue, lastNameInputValue, emailInputValue]);

```

Next is the `submitHandler` which gets called when the form is submitted. Basically what it does is:

1. Prevents a refresh on submit
2. Check that the form is valid
3. If valid, submits and resets the input fields. Otherwise just return(do nothing).

```javascript
  const submitHandler = (event) => {
    event.preventDefault();

    if (!formIsValid) {
      return;
    }

    nameReset();
    lastNameReset();
    emailReset();
  };

```

Now, the next step is optional, it's basically playing with CSS to add classes with colors depending on the forms validity.

```javascript
  const nameInputClasses = nameHasError
    ? "form-control invalid"
    : "form-control";

  const lastNameInputClasses = lastNameHasError
    ? "form-control invalid"
    : "form-control";

  const emailInputClasses = emailHasError
    ? "form-control invalid"
    : "form-control";
```

Check the objects value and if it has any error, change class to `form-control invalid`.

And that's it! 

---

Here's an example of the result:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/83whfbnw6y8692h87lgf.gif)