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
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;
}
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
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
}
}
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
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)
Isnât React built specifically for reusable components? Why adding extra packages to a project? Thanks in advance
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.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.
*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.