DEV Community

Cover image for React custom hooks for everyoneđŸȘ
djibril mugisho
djibril mugisho

Posted on • Edited on

React custom hooks for everyoneđŸȘ

Maintaining react components that contain too much logic has never been easy, thanks to react custom hook, we can distribute our component's logic into a reusable function that can be imported into any component of our application. In case you are new to React, custom hooks are not special functions provided by React, instead, they are built on top of existing hooks.

In this project, we shall build an application to fetch a location based on the provided IP address, this project will be a favorable scenario for learning custom hooks since we practice both reusability and HTTP request within custom hooks.

Create application

In this project we shall be using Vite, if you are new to Vite don't worry everything you know about React remains the same. Run the command below for building your application, and for more information on Vite use the following link

npm create vite@latest myproject -- --template react-t
cd myproject
npm install 

//use the code editor of your choice 
Enter fullscreen mode Exit fullscreen mode

After creating the application, get rid of the App.css or delete all code inside of it, we don't need it in this project, all the styling will be written in the index.css file. To see the custom hooks in action we shall start without them and then change our logic to custom hook later.

css

* {
  margin: 0px;
  padding: 0px;
}

.container {
  max-width: 600px;
  min-height: 300px;
  margin: auto;
  margin-top: 40px;
  border: 0.5px solid rgba(0, 0, 0, 0.127);
  padding: 20px;
}

.container p
{
  margin: 20px 10px;
}

.button-container {
  max-width: 600px;
  margin: auto;
  margin-top: 20px;
  margin-bottom: 50px;
}

.button-container button {
  width: 100%;
  padding: 15px;
}
Enter fullscreen mode Exit fullscreen mode
import { useState } from 'react'

function App() {
  const [loading, setLoading] = useState<boolean>(false);
  const [location, setLocation] = useState<{} | any>();
  const [errorMessage, setErrorMessage] = useState<string>();

  const getLocation = async () => {
    try {
      setLoading(true);
      const request = await fetch("http://ip-api.com/json/"); //provide an ip address as request param if you don't want to use your current ip address e.g:http://ip-api.com/json/24.48.0.1"
      const response = await request.json();
      setLocation(response);
      setLoading(false)
    } catch (error: any) {
      //do what you want with the error 
      setLoading(false)
      setErrorMessage(error.message);
    }

  };

  return (
    <>
      <div className="container">
        <div className="location-info">
           // render location detail
          {location &&
            Object.keys(location).map((key) => {
              return <p><strong>{key}</strong>:  {location[key]}</p>
            })
          }

          {
            loading && <p>...loading</p>
          }
          {
            errorMessage && <p>{errorMessage}</p>
          }
        </div>
      </div>
      <div className="button-container">
        <button onClick={() => getLocation()}>Get location</button>
      </div>

    </>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

As you can the application is working perfectly fine, you click on the button you get the location, and everyone is happy 😎, but what if you want to do the same thing in other components of your application, do you imagine how many duplicated code you will end up with đŸ€”, don't worry let's solve that problem with a custom hook.

In this second example, I'm going to create a useGeoLocation hook that will provide all the states that we need, including the location.

useLocationHook

import { useState } from "react"


export const useGeoLocation = (): { location: any, errorMessage: string, loading: boolean, getLocation: (id?: string) => void } => {
    const [loading, setLoading] = useState<boolean>(false);
    const [location, setLocation] = useState<any>(); //i use any type for this state because i'm not sure about what will be returned;
    const [errorMessage, setErrorMessage] = useState<string>("");

    const getLocation = async (ip?: string) => {
        try {
            setLoading(true);
            const request = await fetch("http://ip-api.com/json/" + (ip ? ip : "")); //provide an ip address as request param if you don't want to use your current ip address e.g:http://ip-api.com/json/24.48.0.1"
            const response = await request.json();
            setLocation(response);
            setLoading(false)
        } catch (error: any) {
            setLoading(false)
            setErrorMessage(error.message);
        }
    }

    return {
        loading,
        location,
        errorMessage,
        getLocation
    }
}
Enter fullscreen mode Exit fullscreen mode

useLocation Hook in action


import { useGeoLocation } from './hooks/useGeoLocation';

function App() {
  const { loading, location, getLocation, errorMessage } = useGeoLocation();

  return (
    <>
      <div className="container">
        <div className="location-info">
          {location &&
            Object.keys(location).map((key) => {
              return <p><strong>{key}</strong>:  {location[key]}</p>
            })
          }

          {
            loading && <p>...loading</p>
          }
          {
            errorMessage && <p>{errorMessage}</p>
          }
        </div>
      </div>
      <div className="button-container">
        <button onClick={() => getLocation()}>Get location</button>
      </div>

    </>
  )
}

export default App

Enter fullscreen mode Exit fullscreen mode

Now you have a portable function that can be used anywhere in your application, you can go a bit deep and use useReducer instead dozen of use states, but since this article is about custom hooks, let's focus on that only.

You may think that states available in the useGeoLocation hook are shared among all components that call it, but that is not the case, each component that calls a custom hook will have its own separate instance of the hook's state, this allows you to use thing like useEffect or useLayoutEffect inside your custom hooks.

Conclusion

Custom hooks đŸȘ can improve the quality of your code and help you with reusable code 😊, use them as much as possible clean code always improves the productivity of your team.

If you find this article useful comment, or why not subscribeđŸ€”. Thanks for reading.

Top comments (4)

Collapse
 
leandro_nnz profile image
Leandro Nuñez

Isn’t React built specifically for reusable components? Why adding extra packages to a project? Thanks in advance

Collapse
 
frontendengineer profile image
Let's Code

you can imagine that useLocationHook is a piece of code that can be used by many React components. It is better use hooks for code reusability and avoid copying/pasting them over and over again.

Collapse
 
leandro_nnz profile image
Leandro Nuñez • Edited

Ok. But react is specifically designed for you to do it by yourself. This package is just a shortcut. Why copying/pasting when you can create your custom hooks and reuse them everywhere?
It seems like it’s built for people without react knowledge only.
Please, if I’m not seeing something, let me know. I just want to understand.

Thread Thread
 
frontendengineer profile image
Let's Code

*But react is specifically designed for you to do it by yourself. *
Yes and No. There are react libraries as well that one can tap to make dev life easier.

*Why copying/pasting when you can create your custom hooks and reuse them everywhere? *
That is what the author just showed everyone. To create a custom hook to promote code reuse

*It seems like it’s built for people without react knowledge only. *
Custom hooks can be easily learned just like any libraries or frameworks. It is not rocket science. Developers that doesn't have capability/eagerness to learn should find another role.