You must have heard about the custom hook in React, and so do I but never really understood how to create. But this time I build a project like Airbnb that helped me to understand it. Let's explore the concept behind it!
But wait !! are the hooks available not sufficient?
yes ! they are, but using their combination will give you magical power (just kidding), it helps to write clean and less code.
Fetching data using the hooks we have
We will develop a component that retrieves and displays a list of all the available rooms
import {useState,useEffect} from "react"
function RoomList() {
const[room,setRoom]=useState([])
useEffect(()=>{
// stays.json : file that contain all room lists
fetch("/stays.json")
.then(data=>data.json())
.then((stays=>{
setRoom(stays)
}))
},[])
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-12">
{
room.map((room) => {
return (
<div>
<picture className=' image-container '>
<img className='w-full h-auto max-h-96 md:max-h-72 object-cover rounded-3xl md:rounded-2xl' src={room.photo} alt="room-photo" />
</picture>
<h3 className='title text-lg font-semibold'>{room.title}</h3>
</div>)
})
}
</div>
)
}
In the above code, we are fetching data from stays.json or using any API to get data, Here I assume that you know how to use the useState and useEffect hooks.
But the above code has not much flexibility like how to fetch data from the different URL in the same component and Everytime we have to use useEffect for fetching in another component as well
Now, let's create ๐ a custom hook
#Example 1-useFetch hook
Now, we will create a custom hook that will solve our problem.
// Fetch.jsx
function useFetch(url){
const [data,setData]=useState(url)
useEffect(()=>{
// stays.json : file that contain all room lists
fetch(url)
.then(data=>data.json())
.then((stays=>{
setData(stays)
}))
},[])
return data
}
export default useFetch
In the above code, useFetch will take the URL as a parameter and it will return data fetched from the API.
After using the custom hook - useFetch
// App.jsx
import {useState,useEffect} from "react"
import useFetch from './Fetch.jsx'
function RoomList() {
// code after using custom hook
const room=useFetch('url')
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-12">
{
room.map((room) => {
return (
<div>
<picture className=' image-container '>
<img className='w-full h-auto max-h-96 md:max-h-72 object-cover rounded-3xl md:rounded-2xl' src={room.photo} alt="room-photo" />
</picture>
<h3 className='title text-lg font-semibold'>{room.title}</h3>
</div>)
})
}
</div>
)
}
As we observe the current state of the code, it is evident that it has become increasingly clean and organized.
#Example 2 -useCustomContext hook
let's say we want to use useContext hook to store our state globally, so at first, we will create a context and export it for another component, it is old way or inefficient way write context
// Context.jsx
import { createContext } from 'react';
const RoomCtx=createContext([])
export default RoomCtx
// App.jsx
import RoomCtx from './Context.jsx'
function App() {
return (
<RoomCtx.Provider value={useFetch('url')}>
<div className="App px-10 md:px-12 lg:px-16 ">
<RoomList />
</div>
</RoomCtx.Provider>
)
}
In the above Component App
, we have provided the context value useFetch('url')
that is returned from the custom hook that we have just created
// App.jsx
import {useState,useEffect,useContext} from "react"
import RoomCtx from './Context.jsx'
function RoomList() {
// code before using custom hook
const room=useContext(RoomCtx)
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-12">
{
room.map((room) => {
return (
<div>
<picture className=' image-container '>
<img className='w-full h-auto max-h-96 md:max-h-72 object-cover rounded-3xl md:rounded-2xl' src={room.photo} alt="room-photo" />
</picture>
<h3 className='title text-lg font-semibold'>{room.title}</h3>
</div>)
})
}
</div>
)
}
In the RoomList
component, for using the context we have to import useContext
and RoomCtx
but it is not a problem for a small application what if our codebase is too large, so every time we need to import useContext
and RoomCtx
that's why we need to create a custom hook so that need not import every time
After using the custom hook - useCustomContext
// Context.jsx
import { createContext } from 'react';
const RoomCtx=createContext([])
export RoomCtx
// creating a custom Hook for Context
function useCustomContext(){
return useContext(RoomCtx)
}
export useCustomContext
Here we have just created a custom hook that will handle the import problem, Despite importing the useContext
and RoomCtx
, we have opted to create our hook and export it.
// App.jsx
import {RoomCtx} from './Context.jsx'
function App() {
return (
<RoomCtx.Provider value={useFetch('url')}>
<div className="App px-10 md:px-12 lg:px-16 ">
<RoomList />
</div>
</RoomCtx.Provider>
)
}
// App.jsx
import {useState,useEffect,useContext} from "react"
import {useCustomContext} from './Context.jsx'
function RoomList() {
// code bofore using custom hook
const {room}=useCustomContext()
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-12">
{
room.map((room) => {
return (
<div>
<picture className=' image-container '>
<img className='w-full h-auto max-h-96 md:max-h-72 object-cover rounded-3xl md:rounded-2xl' src={room.photo} alt="room-photo" />
</picture>
<h3 className='title text-lg font-semibold'>{room.title}</h3>
</div>)
})
}
</div>
)
}
We utilized the useCustomContext
hook to eliminate the need for importing both useContext
and RoomCtx
. This approach can also be applied to other components as necessary.
Conclusion
We have learned how to utilize a custom hook to streamline repetitive code. It's important to note when creating custom hooks, we utilize existing React hooks and return the desired outcome