DEV Community

Cover image for Custom Hook in React: A Comprehensive Guide
ABIDULLAH786
ABIDULLAH786

Posted on • Edited on

Custom Hook in React: A Comprehensive Guide

Introduction

Are you tired of repetitive code in your React components, especially when dealing with fetching data from APIs and managing state? Custom hooks are the solution to your problems!

In this blog, we'll explore how to create two custom hooks: useLocalStorage and useFormValidation. We'll start by building a custom hook called useLocalStoragethat mimics the functionality of useState but persists the state in the browser's localStorage. Then, we'll create useFormValidation, which will help us validate form inputs easily.

Prerequisites:
Before proceeding with this tutorial, make sure you have a basic understanding of React hooks, particularly useState and useEffect. If you're new to hooks, you might want to check out the earlier tutorials in this series.

Custom Hook Scenario 1: useLocalStorage

Step 1: Creating the Custom Hook
In the src directory, create a new folder called hooks. Inside this folder, create a file named useLocalStorage.js.

Let's begin by creating our useLocalStorage custom hook. Remember, custom hooks must start with the prefix use. Open your favorite code editor and set up a new file called useLocalStorage.js.

// hooks/useLocalStorage.js
import { useState } from 'react';

const useLocalStorage = (key, initialValue) => {
  // Get the initial state from localStorage, if available
  const storedValue = localStorage.getItem(key);
  const initial = storedValue ? JSON.parse(storedValue) : initialValue;

  // Create the state variable
  const [value, setValue] = useState(initial);

  // Sync state changes with localStorage
  const updateLocalStorage = (newValue) => {
    localStorage.setItem(key, JSON.stringify(newValue));
    setValue(newValue);
  };

  return [value, updateLocalStorage];
};

export default useLocalStorage;
Enter fullscreen mode Exit fullscreen mode

Step 2: Implementing Custom Hook:
Now that we've created our custom hook in file named as ExampleOne, let's see how to use it in our components. For this example, we'll build a simple form that allows users to input their name and stores it in localStorage.

import React from 'react';
import useLocalStorage from './useLocalStorage';

const NameForm = () => {
  const [name, setName] = useLocalStorage('user_name', '');

  const handleNameChange = (event) => {
    setName(event.target.value);
  };

  return (
    <form>
      <label htmlFor="name">Name:</label>
      <input
        type="text"
        id="name"
        value={name}
        onChange={handleNameChange}
      />
      <p>Hello, {name || 'stranger'}!</p>
    </form>
  );
};

export default NameForm;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. In the useLocalStorage custom hook, we use useState to create a state variable called value. We then initialize it with the value retrieved from localStorage, or with the initialValue provided when the hook is called.
  2. We also define an updateLocalStorage function, which sets the value in localStorage whenever the state changes using setValue.
  3. In the component, we use the useLocalStorage hook by calling it with the key 'user_name' and the initial value '' (an empty string). It returns the name state variable and the setName function, which we use to update the name state whenever the input changes.

Output Example 1

Creating the Custom Hook Scenario 2: useFormValidation

Step 1: Creating the Custom Hook
If you are following this tutorial from start then you may alrady created the hooks folder in src directory if not create it now and then inside this folder, create a file named useLocalStorage.js.

// hooks/useFormValidation.js
import { useState } from 'react';

const useFormValidation = (initialState, validate) => {
  const [values, setValues] = useState(initialState);
  const [errors, setErrors] = useState({});
  const [isSubmitting, setSubmitting] = useState(false);

  const handleChange = (event) => {
    const { name, value } = event.target;
    setValues({ ...values, [name]: value });
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    setErrors(validate(values));
    setSubmitting(true);
  };

  return {
    values,
    errors,
    isSubmitting,
    handleChange,
    handleSubmit,
  };
};

export default useFormValidation;

Enter fullscreen mode Exit fullscreen mode

Step 2: Implementing Custom Hook:
Now, let's create a component in src/compoents/ directory with the name ExampleTwo and there create form where we'll use the custom hook for form validation.


import React from 'react';
import useFormValidation from '../hooks/useFormValidation';
import "./style.css"
const ExampleTwo = () => {
    const initialState = {
        name: '',
        email: '',
        password: '',
    };

    const validate = (values) => {
        let errors = {};

        // Add your validation logic here
        if (!values.name) {
            errors.name = 'Name is required';
        }

        if (!values.email) {
            errors.email = 'Email is required';
        } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email)) {
            errors.email = 'Invalid email address';
        }

        if (!values.password) {
            errors.password = 'Password is required';
        } else if (values.password.length < 6) {
            errors.password = 'Password must be at least 6 characters long';
        }

        return errors;
    };

    const {
        values,
        errors,
        isSubmitting,
        handleChange,
        handleSubmit,
    } = useFormValidation(initialState, validate);


    return (
        <div className='App'>
            <div className='block'>
                <h1>Form Validation Demo</h1>

                <form onSubmit={handleSubmit}>
                    <div>
                        <label htmlFor="name">Name:</label>
                        <input
                            type="text"
                            id="name"
                            name="name"
                            value={values.name}
                            onChange={handleChange}
                        />

                    </div>
                    {errors.name &&
                        <span className="error block">{errors.name}</span>

                    }
                    <div>
                        <label htmlFor="email">Email:</label>
                        <input
                            type="email"
                            id="email"
                            name="email"
                            value={values.email}
                            onChange={handleChange}
                        />
                    </div>
                    {errors.email && <span className="error">{errors.email}</span>}
                    <div>
                        <label htmlFor="password">Password:</label>
                        <input
                            type="password"
                            id="password"
                            name="password"
                            value={values.password}
                            onChange={handleChange}
                        />
                    </div>
                    {errors.password && <span className="error">{errors.password}</span>}
                    <button type="submit" disabled={isSubmitting}>
                        Submit
                    </button>
                </form>
            </div>
        </div>
    );
};

export default ExampleTwo;
Enter fullscreen mode Exit fullscreen mode

Output Example 2

App.js File

import ExampleOne from "./components/customeHookExampleOne";
import ExampleTwo from "./components/customeHookExampleTwo";

function App() {
  return (
    <div>
      <ExampleOne />
      {/* scroll below to view example2 */}
      <ExampleTwo />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Conclusion

Congratulations! You've successfully created and used a custom hooks. Custom hooks are a game-changer when it comes to organizing and reusing logic in React components. They allow you to abstract away complex functionalities and promote code reusability across your application. With useLocalStorageand useFormValidation custom hooks from the GitHub repository, you can now handle state persistence and form validation with ease, keeping your components clean and maintainable.

Connect me on Twitter, Linkedin and GitHub!

Buy-me-a-coffee

Note: This example focuses on the front-end validation to demonstrate custom hooks. In a real-world application, you should also have server-side validation for security purposes.

Next Steps:
Experiment with different use cases and create more custom hooks to handle other common functionalities in your applications. Custom hooks provide an elegant solution to managing complex state and side effects, making your React components more concise and maintainable. Happy coding!

Top comments (2)

Collapse
 
leandro_nnz profile image
Leandro Nuñez

Excellent.Thanks!

Collapse
 
abidullah786 profile image
ABIDULLAH786

Thank you for your kind words! I'm glad you found the blog excellent