How to Make a Custom Hook in React

Share your love

In functional components, we can do everything with the help of hooks, mainly the useState and the useEffect hook, but the power that hooks have given us has gone beyond our imagination. Here we can even make Custom Hook(s) and use them in our apps like other hooks. Pretty similar to the other hooks, these custom hooks have to follow the same rules as the other hooks do.

We will discuss everything regarding custom hooks, how they are made, rules for making custom hooks, etc in this blog.

Let’s start…

Index

What are Custom Hooks in React

According to the documentation, “A custom Hook is a JavaScript function whose name starts with ”use” and may call other Hooks. As both components and hooks are functions, this concept is nothing new and works fine.”

In simple terms, hooks are just like components- javascript functions that are made to make our React App more readable and editable. 

The only difference between them according to us is that the components store both logic and HTML tags or the JSX while custom hooks store only logic or a specific function that might be repeated in the app.

When it comes to creating custom hooks, the sky’s the limit. We can create any hook that we want and use it anywhere in our app while following the rules used to govern other hooks which we will discuss in the next section.

We can use any of the pre-defined hooks inside the custom hook but again we have to follow the rules as they are the basic hooks that React library has provided us with and will not work if defined or used incorrectly.

Just like in a normal hook, every time we call our custom hook the states or side-effects we have used inside it to make its logic are completely isolated or cut off from our main component.  It means that once the hook is called and the logic is rendered, the states and effects will not meddle with other effects or states in the main or any other component. It is completely separated from them and will only act when it’s called.

Rules

  1. Hooks should only be called at the top level of our app and not inside any loops, conditional statements, or functions.
  2. Hooks are a feature of functional components and should not be used in class components
  3. Every custom hook should have a prefix in its name ‘use’. It tells react that this is not a component but a hook and should follow the rules of hooks that are defined above.
  4. You cannot call hooks inside functions but custom hooks are an exception to that

Why Custom Hook in React?

Lets’ understand it this way, we divide our main component into several other components and pass states and functions between them as props to make our code more readable and understandable by not only developers but others as well. 

Custom hooks work in the same way but rather than dividing the code into small pieces it is used to isolate a specific logic that we have to use at any time throughout the production of our app and call it when it’s needed in every component just by writing a single line of code. Isn’t that great? 

Like a hook to fetch data from an API,  we call it in any component with just a single line of code, and not bound by a specific API just by the argument url which we can change every time

But, it is not only used for separating the most used logic from the function but can also be used to separate pieces of code that might be a bit confusing without a heads up of what’s going on.

Consider a scenario, if we have a component in which we have two or more separate pieces of useEffect logic going on, it’s better to put them into separate custom hooks and name them, even if this logic will not be shared between other components.

This is because it is far easier to read and understand the logic in this way rather than reading multiple lines of strings of useEffect hook and understanding them with the component.

And as we are free to give them any name we want (starting with use), it becomes easier to understand the logic used inside that particular custom hook as just by reading the name one can tell or guess the logic inside the custom hook. We think this approach is better than explaining every line with comments.

And unlike a React component, a custom Hook doesn’t need to have a specific signature. We can decide what it takes as arguments and whether it should have return statements or not. In other words, it’s just like a normal function

Now let’s see this hook in action.

What are we up to?

Now we know the what, why, and when of the custom hook, let’s talk about the how part.

In this blog, we will make an App that fetches GIFs from a website called Giphy through its API and the Access Key which will be generated automatically as we sign up for an account and fill in the required details regarding the app.

You can visit the GitHub Repo for more details like the code or the API and also see the final product

Creating the React App

It’s easy to create a React App – go to the working directory in any IDE and enter the following command in the terminal.

npx create-react-app custom-hook

If you are unsure how to properly set up a create-react-app project you can refer to the official guide here at create-react-app-dev.‌‌

After the setup, run npm start in the same terminal to start the localhost:3000 where our React app will be hosted. We can also see all our changes there.

Working on the UI part

For the sake of demonstrating the use of a custom hook, we have made two components fetching data from the same API, but one of them is fetching some random GIFs, while the other is fetching GIFs based on the search query using the input field as shown.

import React from "react";
import Random from "./Components/Random";
import Tag from "./Components/Tag";
const App = () => {
  return (
    <>
      <div className="container-fluid">
        <div className="row text-center mt-5">
          <div className="d-flex justify-content-center">
            <h1 className="fs-2 fw-bold p-2 w-75">Random GIF Application</h1>
          </div>
          <div className="d-flex justify-content-center mt-5">
            <Random />
            <Tag />
          </div>
        </div>
      </div>
    </>
  );
};
 
export default App;

Our main reason for writing this blog is to explain the concept of custom hooks through an example, thus we will not be explaining the styling part of our app.

The Random Component

This component, as told, will fetch random data from Giphy’s API and display it using the img tag in our app.

As always the API will be called inside the useEffect hook which will be called at the top level of our app and used as per the rules of Hooks. The code is as follows-

import React, { useState, useEffect } from "react";
 
const API_KEY = process.env.REACT_APP_API_KEY;
 
const Random = () => {
  const [gif, setGif] = useState("");
 
  const fetchGif = async () => {
    const initial = await fetch(
      `https://api.giphy.com/v1/gifs/random?api_key=${API_KEY}`
    );
    const data = await initial.json();
    const final = data.data.images.downsized_large.url;
    setGif(final);
  };
 
  useEffect(() => {
    fetchGif();
  }, []);
 
  const newGif = () => {
    fetchGif();
  };
return (
    <>
      <div className="container-fluid">
        <div className="row">
          <div className="d-flex justify-content-center">
            <h1 className="fs-2 fw-bold p-2 w-75">Random GIF</h1>
          </div>
          <figure className="w-100 mt-4">
            <img src={gif} alt="random gifs" className="w-75" />
          </figure>
          <div className="d-flex justify-content-center my-4">
            <button
              className="text-center fs-5 fw-bold text-capitalize px-4 btn-secondary text-white py-2"
              onClick={newGif}
            >
              New Random Gif
            </button>
          </div>
        </div>
      </div>
    </>
  );
};
 
export default Random;

In the above code, we have a file ‘.env’ in which we have stored our Access_Key which we are dynamically getting in our Random Component.

We have initialized the state as an empty string with its updating function using the useState hook for fetching and storing the fetched GIF in it and displaying it in our App.

There are many methods in both JavaScript and React through which we can make an API call and fetch the data into the state we have already made and discussed above.

In the UI part of our APP, we have an h1 tag, an image tag, and a button with an onClick event handler which will trigger the fetchGIF() function to make the API call and get the random GIF which will be displayed using the img tag.

The Query Component

In this component, everything’s the same except for the additional parameter in the API and the input tag with the value attribute, and the onChange event handler in the UI part of the component.

As told, this component will not fetch random GIFs, it will fetch query-based GIFs which will be stored and used using the input tag and the value attribute with the onChange event handler as told.

import React, { useState, useEffect } from "react";
 
const API_KEY = process.env.REACT_APP_API_KEY;
 
const Query = () => {
  const [query, setQuerry] = useState("dogs");
  const [gif, setGif] = useState("");
 
  const fetchGif = async () => {
    const initial = await fetch(
      `https://api.giphy.com/v1/gifs/random?api_key=${API_KEY}&tag=${query}`
    );
    const data = await initial.json();
    const final = data.data.images.downsized_large.url;
    setGif(final);
  };
 
  useEffect(() => {
    fetchGif();
  }, []);
 
  const newGif = () => {
    fetchGif();
  };
return (
    <>
      <div className="container-fluid">
        <div className="row">
          <div className="d-flex justify-content-center">
            <h1 className="fs-2 fw-bold p-2 w-75 text-capitalize">
              Randon {tag} GIFs
            </h1>
          </div>
          <figure className="w-100 mt-4">
            <img src={gif} alt="random gifs" className="w-75" />
          </figure>
          <div className="d-flex justify-content-evenly my-4">
            <input
              type="text"
              value={query}
              onChange={(e) => setQuerry(e.target.value)}
              className="text-center fs-5 fw-bold text-capitalize py-2 border-0"
            />
            <button
              className="text-center fs-5 fw-bold text-capitalize px-4 btn-secondary text-white"
              onClick={newGif}
            >
              New {tag} GIF
            </button>
          </div>
        </div>
      </div>
    </>
  );
};
 
export default Query;

The output will be as below

Custom Hook in React
the output of the custom hook

Updating our App with the Custom Hook

We have seen that in both Random and Query Components we have used the same set of codes for fetching the data from the API, the only difference was the query parameter that we added at the end of the API in the Query Component.

This was just two to three lines of codes and just two components in which it was used. But what if there were more lines of code and that to some complex ones or we have more than two components in which we have to make the same API call but with a different parameter. or with a couple of them.

In such cases, it is not a wise decision to just copy the same piece of code and paste it into every component. 

What we can do is make a custom hook to isolate or separate the logic which has to be repeated in many components and call it with a single line of code making the component easier to read.

The reasons for making a custom component can be many, but the main question is how to make one?

How to make a custom component

As per the rules, every custom component should have a unique (it is not mandatory but a wise decision as it helps both the developer and the reader to understand in brief what that hook is about) name starting with the prefix ‘use’.

For this app, we have named our custom hook- useFetch as it will be used to fetch data from the API and store it in a state.

import { useState } from "react";
 
const useFetch = (url) => {
  const [gif, setGif] = useState("");
 
  const fetchGIF = async () => {
    const initial = await fetch(url);
    const data = await initial.json();
    const final = data.data.images.downsized_large.url;
    setGif(final);
  };
  return [{ gif }, fetchGIF];
};
 
export default useFetch;

The code above is the same code of the function used to fetch data from API with the same state and updating function from both the components.

The only difference is that we have not defined the API here and just passed an argument url in place of the API which has the API stored in it. 

We are not using the argument url as props, so don’t try destructuring it or writing props.url, the app will not work.

As told, we can use any type of hook from the list of predefined hooks that the React library has provided us with. In this case, we have used the useState hook which is exactly the same as the one we used in both the components.

As every hook is a javascript function and every javascript function must have a return value, our custom hook also has a return value which is returning the state which is initialized as an empty string, and the function back to the main component.

Updating the Components

We have seen how to make a custom hook, now let’s see how we will use this custom hook inside our components.

We have to write a single line of code in both the components with some parameters if required like a search query in the API in place of the fetchGIF function in both the components.

// just add the parameter where required and if not required just use the API as it is
  const [{ gif }, fetchGIF] = useFetch(
    `https://api.giphy.com/v1/gifs/random?api_key=cBwaHo66byL5Sw2g5fkdboXB4wtASK7R&tag=${tag}`
  );

Notice that the line which we have written resembles the line we write for the useState hook when we initialize it in our apps – a state and a function separated by a comma. The difference is only that here the function is not used to update the state but rather to call the function itself inside another function or to play the recursive game.

We have defined the API in the component rather than in the custom hook because the API will not remain the same in all cases as we will add some parameters to it to make it more specific on what exactly we want to display in our UI. 

And as per the rules we have to call the hook at the top level of our component. But the custom hook will not be called the same way it was for all the pre-defined hooks, it will be called like any other component as shown

import React, { useState, useEffect } from "react";
import useFetch from "../Custom Hooks/useFetch";

Now our components will look like this 

import React, { useEffect } from "react";
import useFetch from "../Custom Hooks/useFetch";
 
const Random = () => {
  const [{ gif }, fetchGIF] = useFetch(
  `https://api.giphy.com/v1/gifs/random?api_key=cBwaHo66byL5Sw2g5fkdboXB4wtASK7R`
  );
 
  useEffect(() => {
    fetchGIF();
  }, []);
 
  const newGif = () => {
    fetchGIF();
  };

As for the output of the app, it will not change. This is because we haven’t done any major changes in our app for it to have a different outcome.

Conclusion

Custom hooks are very useful when it comes to making our React apps not only dynamic but easy to read and understand, and with its naming convention it allows us to give our custom hook any name we want which makes it easier to understand what the logic of that custom hook will be about.

In this blog, we learned what and why of custom hooks and how to make a custom hook without changing or adding any extra complicated line of code in our app and update the current component in which it will be used with just a single line of code to call the custom hook, making our app much easier to understand.

Frequently Asked Questions

What are Custom Hooks in React

Custom Hooks are nothing more than javascript functions that are used to store logic that is going to be used many times in our app

Why do we use Custom Hooks in React

The main reason they are used is to make our code more readable and understandable but there are several other reasons like making the custom hook of a logic that will be used again and again in our app to avoid writing all those lines every time.

Can we call other hooks inside Custom Hooks

Yes, we can call and use other hooks inside custom hooks but under the jurisdiction of React guidelines.

Share your love