Understanding React custom Hooks

In React there are several built-in Hooks like useState, useEffect, and useContext. Sometime you need to create your own Hooks for specific purpose like checking user session, fetching data, or showing notifications. For these type of application specified purpose you can create custom Hooks in React. So basically, Hooks are the functions using in the React to do a particular task.

Creating a Custom Hook

As mentioned earlier custom hook is simple JavaScript function whose name start with use and can call other hooks. In the below example you can see a custom Hook useFetch used to call an API to populate data from the backend.

useFetch.js
// custom hook
const useFetch = () => {

  const sendRequest = async (requestConfig) => {
    try {
        const apiResponse = await fetch(requestConfig.url, {
            method: requestConfig.method ? requestConfig.method : "GET",
            headers: requestConfig.headers ? requestConfig.headers : {},
            body: requestConfig.body ? requestConfig.body : null,
        });

        if (!apiResponse.ok) {
            return {
                data: null,
                error: `An error has occured: ${apiResponse.status}`
            };
        } else {
            const photos = await apiResponse.json();
            return {
                data: photos,
                error: null
            };
      }
    } catch (error) {
      return {
        data: null,
        error: error.message
      };
    }
  };

  return {
    sendRequest
  };
};

export default useFetch;

Using a Custom Hook

Here we are using the above custom hook in the below component to fetch the data.

PhotosHome.js
import { useEffect, useState } from 'react';
import { apiKey } from '../configs/config.env'; 
import useFetch from '../hooks/useFetch'; // Custom hook for making HTTP requests
import SearchPhotos from './SearchPhotos'; 

export default function PhotosHome() {
    const [photos, setPhotos] = useState([]);
    const [error, setError] = useState('');

    // Destructure the sendRequest function from the useFetch custom hook
    const { sendRequest } = useFetch();

    // Function to load photos initially when the component mounts
    async function fetchPhotos() {
        // Fetch photos using the API
        const { data, error } = await sendRequest({
            url: `/photos/?client_id=${apiKey}` // URL endpoint with the API key
        });

        setPhotos(data);
        setError(error);
    }

    // useEffect hook to fetch photos when the component mounts
    useEffect(() => {
        fetchPhotos(); 
    }, []); 

    // Function to search photos based on a query
    async function searchPhotos(searchQuery) {
        // Fetch search results using the API
        const { data, error } = await sendRequest({
            url: `/search/photos/?client_id=${apiKey}&query=${searchQuery}`
        });

        setPhotos(data.results);
        setError(error);
    }

    return (
        <>
            <SearchPhotos searchPhotos={searchPhotos} />
            <br />

            {
            error === null ? (
                photos.map((photo) => {
                    return <img key={photo.id} src={photo.urls.small} 
                    alt={photo.description || 'Photo'} />;
                })
            ) : (
                <p>{error}</p>
            )
            }
        </>
    );
}

Things to remember

  • Custom hooks let share you logic between components.
  • Custom hook only share stateful logic, not the state itself.
  • Custom hook must be named starting with use and followed by a capital letter.
  • Custom hooks should be pure functions.
  • All custom hooks will re-render when your component re-renders.

Benefits of Custom Hooks

  • Reusability
    By using a custom Hook in the component you can avoid the repetitive logic. As in the example shown above, you can use a custom hook to fetch data from backend, and use the same in various places.

  • Encapsulation
    You can hide the complex implementation logics in custom hooks and provide a simple API for consuming by the components.

  • Clean Code
    By abstracting complex and repetitive logics to custom hooks makes your code cleaner and more readable. This seperation allows you to focus on the functionality and make your components more focused on rendering the UI.

Summary

Custom hook is a JavaScript function and It must be named starting with use followed by a capital letter. They are powerful way to encapsulate and reuse logic while giving the benefits sunc as clean code, more readability and flexibility.

Read More