Introduction
In this article, I am going to explain how to create a custom Hook for API calls in React.
React Hooks
It is a new feature that allows using React features without writing a class. Hooks are functions that help to use the state and lifecycle features in the React function components. Hooks do not work inside the class components. This feature has introduced in React 16.8.
We need to follow the below rules to use Hooks in the React
- Only call Hooks at the top level
Hooks should always be used at the top level of the React functions. Do not call Hooks inside the loops, conditions, or nested functions.
- Only call Hooks from React functions
Cannot call Hooks from regular JavaScript functions. Instead, Call Hooks from React function components. Hooks can also be called custom Hooks.
Custom Hooks
Custom Hooks are used for building common or reusable logic and these functions are prefixed with the word "use". Ex. useTestCustomHook.
I am using typescript for implementing the custom hook in React. I have two interfaces which are IRequestInfo and IResponseInfo.
IRequestInfo
IRequestInfo defines how the request format should be. It has below properties.
Headers
Request headers like authentication, content-type, etc.
Method
It represents the API Method such as "GET" or "PUT" or "POST" or "PATCH"
EndPoint
It is the API endpoint going to call.
RequestBody
Request Body for the API call. It is an optional one. Because the GET call doesn't have a request body.
export interface IRequestInfo {
Headers?: {};
Method: string; // "GET" or "PUT" or "POST" or "PATCH"
EndPoint: string;
RequestBody?: object;
}
IResponseInfo
IResponseInfo defines the format of the response and it has below two properties.
Data
It contains the response data or error.
hasError
It is true if the fetch is failure otherwise false. We can identify the API status using this property.
export interface IResponseInfo {
Data: any;
hasError: boolean;
}
I have created one custom hook function "useFetchCall".
Inside the "useFetchCall" hook,
- Used the fetch() function to call the API
- The hook has one argument for the initial request value.
- The hook has three return values
- response - It has the response of the API which is the IResponseInfo type.
- isFetching - It is true when calling the API and false once the response came from API
- setRequest - Set the request for API call which is IRequestInfo type.
- When calling the setRequest() function from any function component or other custom hooks, it will assign the request data to "requestInfo" local state inside the "useFetchCall" hook.
- Once value is assigned to "requestInfo" local state then, the useEffect() will be execute. Because, "requestInfo" has added as dependecy for that useEffect().
- The API calling logic will be executed inside of the useEffect().
Once an API call has successfully completed, then assign the API's response to "responseInfo" local state using "setResponse()" method. Ex. setResponse({ Data: httpResponse, hasError: false}),
export enum HttpStatusCode {
OK = 200,
CREATED = 201,
SUCCESS_NO_CONTENT = 204
}
function useFetchCall(props: IRequestInfo): [IResponseInfo, boolean, React.Dispatch<React.SetStateAction<IRequestInfo>>] {
const [isFetching, setIsFetching] = useState(false);
const [requestInfo, setRequest] = useState(props);
const [responseInfo, setResponse] = useState({} as IResponseInfo);
useEffect(() => {
if (Object.keys(requestInfo).length === 0 && requestInfo.constructor === Object)
return;
const promise = new Promise((resolve: any, reject: any) => {
const fetchURL = requestInfo.EndPoint;
const fetchData = {
body: requestInfo.RequestBody ? JSON.stringify(requestInfo.RequestBody) : "",
headers: requestInfo.Headers ? requestInfo.Headers : {},
method: requestInfo.Method
};
fetch(fetchURL, fetchData).then((response: Response) => {
switch (response.status) {
case HttpStatusCode.OK:
case HttpStatusCode.CREATED:
case HttpStatusCode.SUCCESS_NO_CONTENT:
response.clone().json().then((data: any) => {
resolve(data);
}).catch(() => {
// Log the Error
resolve(null);
});
break;
default:
response.clone().json().then((data: any) => {
reject(data);
}).catch(() => {
// Log the Error
reject(null);
});
}
}).catch((error: Error) => {
// Log the Error
reject(error);
});
});
setIsFetching(true);
promise.then(
(httpResponse: any) => {
setResponse({ Data: httpResponse, hasError: false, });
setIsFetching(false);
},
(error: Error) => {
setResponse({ Data: error, hasError: true, });
setIsFetching(false);
});
}, [requestInfo]);
return [responseInfo, isFetching, setRequest];
}
export default useFetchCall;
How to Call "useFetchCall" Custom Hook
Declare and Initialize the "useFetchCall" hook
const [response, isFetching , setRequest] = useFetchCall({} as IUseHttpRequestInfo);
Create the request
const apiRequest = // Build you request based on "IRequestInfo" type.
Call the API using Custom Hook by setting the request
setRequest(apiRequest);
Read the API response
React.useEffect(() => {
if (isFetching === false && response && response.Data)
// Implement the logic and read the response from "response.Data" object
}, [response]);
React.useEffect(() => {
if (isFetching === true)
// Implement the logic loading
}, [isFetching]);
Hope you liked it and know about custom hooks in react. If you have any doubts or comments about this, please let me know in the comments.