Mastering Reusability in React: A Guide to Crafting Custom Hooks for Stateful Logic

Photo by Don Kaveen on Unsplash

Mastering Reusability in React: A Guide to Crafting Custom Hooks for Stateful Logic

Introduction

Are you tired of writing the same code over and over again in your React applications? Have you ever wanted to reuse some of your existing code across different components, without having to repeat yourself? Great, with custom hooks, you can extract reusable logic from your components, making your code more organized, maintainable, and easy to understand.

In this article, we will dive into the world of custom hooks. We will start with the fundamentals and progress to techniques for crafting custom hooks and by the end, you will be well on your way to mastering reusability in React.

What are custom hooks?

According to the React documentation, a custom hook is a JavaScript function that starts with the prefix “use”. It is similar to other React hooks but specifically designed to extract component logic and make it reusable across multiple components.

Imagine you are building this fancy e-commerce application and you need to fetch some products to display on a page. You write all the stateful logic to get the products, but then you realize you need to display even more products on some other pages. You do not want to copy and paste everything because that is just not cool. So, what do you do then?

You can simply use custom hooks to handle all that product-fetching logic in one component and reuse it across different components in your application.

The rules for naming custom hooks

Naming custom hooks is part of making them effective. So, before diving into using custom hooks, here are a few things you should know to ensure that you are on the right path.

  1. Custom hooks begin with the prefix “use”.

  2. The naming convention of a custom hook should be descriptive.

  3. It should be in camelCase.

  4. It should not contain any underscores.

  5. All Custom hooks should be easy to use and understand.

Crafting your first custom hook

Alright, let us get our hands dirty and create our very own custom hook. I recently stumbled upon this Rest API from fakeStoreApi. It has all sorts of cool endpoints, like men’s clothing, women’s clothing, gadgets, accessories, and user data. Since we are learning about custom hooks, building a basic fetch functionality with these endpoints will help you master this concept.

Think about it, with a custom hook called ‘useFetch’, you can handle all your product fetch from the API endpoint and then reuse the same fetch logic in any other components where you would need to fetch some more products in your application.

Begin by creating a customhooks folder in your React source directory. This will keep your code organized and make it easier to find and use any custom hooks. Create a useFetch file in this folder to handle all of the logic for fetching data from the API, including error handling and loading states. Your project directory should look like this:

Next, we would be crafting a custom hook that uses the useEffect hook to make a fetch request to a particular URL. You might need to have a little knowledge of Asynchronous JavaScript but if you don't, I'd do my best to comment out each section of the code.

//importing react hooks
import { useState, useEffect } from "react";

const useFetch = (URL) => {

//defining the states
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {

//product fetch using async and await
    const productsFetch = async () => {
      try {
        const res = await fetch(URL);
        const data = await res.json();
        console.log(data);

//updating the states
        setData(data);
        setLoading(false);
      } catch (error) {
        setError(error.message);
        setLoading(false);
      }
    };

//invoking the productFetch function

    productsFetch();
  }, [URL]);

  return { data, loading, error };
};
exportdefault useFetch;

If the request fails, an error message is displayed and the loading state is set to false. If the request is successful, data is set to the received data, the error is set to null, and the loading state is set to false. The hook then returns an object containing the data, loading, and error states, which can be destructured and used in any component that needs to fetch data from any URL. Let's take a look:

//Women.js
import useFetch from "./customHooks/useFetch";
function WomenCollection() {
  const {
    data: products,
    loading,
    error,
  } = useFetch("https://fakestoreapi.com/products/category/women's clothing");
  return (
    <main>
      {error && <p>{error}</p>}
      {loading && <p>...products are being fetched</p>}
      {products &&
        products.map((item) => (
          <section key={item.id} className="display">
            <figure>
              <img src={item.image} alt="products" style={{ width: "4rem" }} />
            </figure>
            <figcaption>
              <h4>{item.title}</h4>
              <p>${item.price}</p>
            </figcaption>
          </section>
        ))}
    </main>
  );
}

export default WomenCollection;

Import the custom useFetch hook first, then destructure the stateful logic from the UseFetch function with the API endpoint as an argument. The collected data is then used to display product information such as an image, name, and price. If an error occurs while retrieving the data, an error message will be displayed, and if the request to the API is still being processed, a loading message will be displayed.

As long as I have a reusable hook “useFetch”, every time I needed to fetch data from a different endpoint I need not write another stateful logic for the entire process again. All I will have to do is to import my crafted custom hook called “useFetch”, destructure the returned values and then change the URL argument. Let’s try that, shall we?

//Men.js
import useFetch from "./customHooks/useFetch";
function MenCollection() {
  const {
    data: products,
    loading,
    error,
  } = useFetch("https://fakestoreapi.com/products/category/men's clothing");
  return (
    <main>
      {error && <p>{error}</p>}
      {loading && <p>...products are being fetched</p>}
      {products &&
        products.map((item) => (
          <section key={item.id} className="display">
            <figure>
              <img src={item.image} alt="products" style={{ width: "4rem" }} />
            </figure>
            <figcaption>
              <h4>{item.title}</h4>
              <p>${item.price}</p>
            </figcaption>
          </section>
        ))}
    </main>
  );
}

export default MenCollection;

I changed the URL argument, and everything worked as it should, awesome! I just saved myself some time and energy by not having to rewrite the entire fetch logic a bunch of times.

Application of custom hooks

Asides from data fetching, there are other scenarios where custom hooks can be used to manage state and handle specific functionality in a React application. Such scenarios include:

  • Form Management: Custom hooks can manage the state of a form and handle form submissions, making it easier for you to handle user input and validation all through your React application.

  • Pagination: With custom hooks, you can reuse the pagination feature for your application, making it easier for you to ensure that your users have a smooth experience navigating through large amounts of data.

  • Search Feature: Custom hooks can help you create a reusable debounced search feature, which limits the number of requests made to an API as a user types, and with this, you can make the search processes in an application more efficient.

  • Custom Animation: Using Custom hooks, you can create custom animations and transitions that can be reused several times in your React application.

Conclusion

So, you have learned a bit about custom hooks and how they can make your code super reusable and organized. Cool, right? Now, it is time to get creative and start experimenting with them. Save yourself a whole ton of time and energy by using custom hooks in your next React project.