React Custom Hooks: How To Build your own useRequest Hook.

I’m a Frontend developer and Technical writer; with a passion for exploring new technologies and writing about them.
Introduction
One of the most important and unique features of React is the introduction of Hooks. The React Hooks are JavaScript functions that are used to Isolate reusable parts of components. In simpler terms, they are functions you can call over and over again in your components to perform specific tasks. To use the hooks in your components, you need to import them. There are generally two classes of Hooks in React, viz: The Built-in Hooks and The Custom hooks.
The built-in hooks are the hooks that are provided by the React library, they are named with the prefix use e.g useState ,useEffect ,useContext etc. In this article, you'll learn what custom hooks are and how to build your own useRequest hook for data posting and fetching in your React App.
Prerequisites
To properly understand this article you must have a basic knowledge of JavaScript, HTTP Requests, React and React-Hooks. If you are struggling with HTTP Requests in JavaScript, check out this article I wrote to simplify it.
But do not fret, I'll break the concept down to the smallest bits.
What are Custom Hooks?
Custom Hooks are reusable JavaScript functions, built by you- the developer- to perform a particle task in your React components. Conventionally the custom hooks are named with camelCase using the prefix use.
You may be wondering, Why do I need to build a custom hook? Well, Custom Hooks are typically built to handle side effect tasks such as fetching data, setting timers, saving data to local storage etc. They are built to handle functions that do not render or update DOM elements directly.
Guide to Creating a useRequest Hook
Initialize Your React App
Create a new React app using create-react-app in your terminal as shown below:
npm create-react-app
Create a Hook folder in the Root Directory
In your React App's root directory, create a new folder named Hooks. This folder will hold and manage all the custom hooks in your app
Create a useRequest.js File
In your Hook folder create the file useRequest.js, It is this file that will contain the logic for your custom useRequest hook. Do not forget to import your React dependencies!
Create a useRequest Function
In the useRequest.js file initialize a function named useRequest
const useRequest=()=>{}
Now, Compose the Hook with logic step-by-step.
Firstly, since you need a hook that sends HTTP requests, build an HTTP request function
requestHandlerusingfetchAPI- or any other one you're comfortable with- in theuseRequestfunctionconst useRequest=()=>{ async function requestHandler(configData,applyData){ try { const response = await fetch(// url will later be added here , { method: // Any HTTP Methode body: // Body may or may not be use since we may need to GET headers: //Any HTTP header }) if (!response.ok) { throw new Error("Something went wrong")//Checks If response is ok } } catch (error) { console.log(error) } } }In the code snippet above, the
useRequestfunction has been populated with a functionrequestHandlerwhich contains the logic to send HTTP Request. The function takes two argumentsconfigDataandapplyData, the first argumentconfigDatais an object containing HTTP Requests methods, headers etc. While the second argumentapplyDatais a JavaScript function that consumes the response data. Notice how thefetchmethods have been left without values? Well, this is because theuseRequestHook should be reusable thus, these values should not be hard coded.Secondly, you need to handle states. What does this mean? well, take for example, If the HTTP request fails you need to inform the user their request has failed, or if the request is being loaded you also need to inform your users appropriately; nobody likes to submit a request and not get a response on what is currently happening. In this Hook, you should handle two states, which are:
isLoadinganderrorThese states handle loading and error respectively as shown in the code snippet below.//since we need the useState hook, we need to import it import { useState } from "react"; const useRequest=()=>{ const [error, setError] = useState(null) const [isLoading, setIsLoading] = useState(false) async function requestHandler(configData,applyData){ setIsLoading(true); //to indicate when request starts processing setError(null)//Initialise Error as null before every request try { const response = await fetch(// url will later be added here , { method: // Any HTTP Methode body: // Body may or may not be use since we may need to GET headers: //Any HTTP header } ) if (!response.ok) { throw new Error("Something went wrong") //Checks If response //is okay, throws an error if its not } } catch (error) { setError(error) //set error and error message } setIsLoading(false)//to indicate when requests stops processing } }Thirdly, your
requestHandlerfunction should getconfigDataas a parameter. This parameter is a JavaScript object that must be passed when calling therequestHandlerhook anywhere in your code, It should containurlmethodbodyandheaderfields to configure thefetchAPI request object(the second parameter)import { useState } from "react"; const useRequest=()=>{ const [error, setError] = useState(null) const [isLoading, setIsLoading] = useState(false) async function requestHandler(configData, applyData){ setIsLoading(true); //to indicate when request starts processing setError(null)//Initialise Error as null before every request try { const response = await fetch( configData.url, { method: configData.method; //to check if body field is true asin PUT and POST request body: configData.body? JSON.stringify(configData.body): null; headers: configData.header; } ); if (!response.ok) { throw new Error("Something went wrong")//Checks Ifresponse is ok } } catch (error) { setError(error) } setIsLoading(falsely) } }while defining the
configDataobject, it is important to set the value ofbodytofalseif you're not attaching a payload e.g inGETrequests.Fourthly, the Addition of the
returnstatement, you typically want to return the state management variables such aserrorloadingand the request function -requestHandler-in an object. This is illustrated in the code below:import { useState } from "react"; const useRequest=()=>{ const [error, setError] = useState(null) const [isLoading, setIsLoading] = useState(false) async function requestHandler(configData,applyData){ setIsLoading(true) setError(null) try { const response = await fetch( configData.url, { method: configData.method; //to check if body field is true asin PUT and POST request body: configData.body? JSON.stringify(configData.body): null; headers: configData.header; }); if (!response.ok) { throw new Error("Something went wrong") //Checks If response is okay } } catch (error) { setError(error) } setIsLoading(false) } return {error, isLoading, requestHandler} }the returned variables can then be used for state management and conditional rendering in your app. for example you can create a modal that shows "Loading..." when the
isLoadingvariable istrue.Next, handle the
fetchresponse data by passingresultintoapplyDatafunction.import { useState } from "react"; const useRequest=()=>{ const [error, setError] = useState(null) const [isLoading, setIsLoading] = useState(false) async function requestHandler(configData, applyData){ setIsLoading(true) setError(null) try { //handling the http request const response = await fetch( configData.url, { method: configData.method; //to check if body field is true asin PUT and POST request body: configData.body? JSON.stringify(configData.body): null; headers: configData.header; } ); //handling Data const result= await response.json() applyData(result) //Handling error if (!response.ok) { throw new Error("Something went wrong")//Checks If response isOk } } catch (error) { setError(error) } setIsLoading(false) } return { error, isLoading, requestHandler } }Next, memoize the
requestHandlerfunction using the built-in React Hook-useCallback.To ensure the function only changes when one of its dependencies changes. TheuseCallbackis likeuseEffectbut for functions, it takes two arguments: The callback function and an array of dependencies. Also, you need to refactor yourrequestHandlerfunction to a function declaration syntax, to use theuseCallbackhook.import { useState } from "react"; import {useCallback} from "react" const useRequest=()=>{ const [error, setError] = useState(null) const [isLoading, setIsLoading] = useState(false) const [data,setData] = useState(null) const requestHandler= useCallback(async (configData, applyData)=>{ setIsLoading(true) setError(null) try { //handling the http request const response = await fetch( configData.url, { method: configData.method; //to check if body field is true asin PUT and POST request body: configData.body? JSON.stringify(configData.body): null; headers: configData.header; } ); //Handling error if (!response.ok) { throw new Error("Something went wrong") //Checks If response // is okay, it throws an error if it"s not. } //handling Data const result= await response.json() applyData(result) }) } catch (error) { setError(error) } setIsLoading(false) }, [] ) return { error: error, isLoading: isLoading, requestHandler:requestHandler } }In this example, the array of dependencies has been left empty because the request needs to be sent once.
Finally, export the useRequest hook. The final step is to export the useRequest hook so you can use it in anywhere where in your app after import
import { useState } from "react"
import {useCallback} from "react"
const useRequest=()=>{
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(false)
const requestHandler= useCallback(async (configData, applyData)=>{
setIsLoading(true)
setError(null)
try {
//handling the http request
const response = await fetch( configData.url,
{
method: configData.method;
//to check if body field is true asin PUT and POST request
body: configData.body? JSON.stringify(configData.body): null
headers: configData.header;
}
);
//Handling error
if (!response.ok) {
throw new Error("Something went wrong") //Checks If response
// is okay, it throws an error if it"s not.
}
//handling Data
const result= await response.json()
applyData(result)
})
} catch (error) {
setError(error)
}
setIsLoading(false)
}, [] )
return {
error: error,
isLoading: isLoading,
requestHandler:requestHandler
}
}
export default useRequest;
after this, you'll be able to access your useRequest hook in any component as shown in the code snippet below:
import React from "react";
import useRequest from "./hook/useRequest.js"
function App(){
const {error,isLoading,requestHandler}= useRequest()
}
Summary
In the previous section, you have learned using a step-by-step guide, how to create a useRequest custom hook. Below is the full code without the extra annotations.
import { useState, useCallback } from 'react';
const useRequest = () => {
const [error, setError] = useState(null)
const [isLoading, setIsLoading] = useState(false)
const requestHandler = useCallback(async (configData, applyData) => {
setIsLoading(true)
setError(null)
try {
const response = await fetch(configData.url, {
method: configData.method,
body: configData.body ? JSON.stringify(configData.body) : null,
headers: configData.header
})
if (!response.ok) {
throw new Error('Something went Wrong')
}
const result = await response.json()
applyData(result)
} catch (errpr) {
setError(error)
}
setIsLoading(false)
}, []);
return {
error: error,
isLoading: isLoading,
requestHandler: requestHandler,
}
}
export default useRequest;
There you have it, folks, you have learned how to create a custom Hook to handle HTTP requests in React. Feel free to practice creating other custom Hooks.
Like, share and leave a comment below. until next time folks; code(), eat(), sleep() and repeat().



