Prerequisite to implement the customHooks
- Knowlegde of functional components, and react hooks.
- Libraries/Packages required: Redux, Axios.
- Placeholder API such as: jsonplaceholder
Topics covered in this blogpost:
- Architectural pattern that is used for creating custom hook with axios.get().
- Implementation of custom hook.
- Usage in the actual component.
- More usecases for the custom hook like addition of adhoc redux actions, and multiple REST method implementation.
Architecture of our custom hook.
- Every custom hook generally returns a state, it may or may not have a useEffect inside it. Let us name our custom hook as
useFetch
. According to the Rules of Hook every custom hook should haveuse
as a keyword in front of it to recognize it as a hook. -
useFetch
is going to consist of the following blocks:- Parameters:
- List of parameters such as URL, the method type, bodyand, headers.
- State Block:
- This will consists of all the local states i.e.
useState
- useEffect Block:
- This will consists of the logic for
axios
call that we are going to make to the server.
Below diagram will provide a clearer view on how useFetch
is designed:
Implementing our custom hook: useFetch
- Let us first create a function which accepts a url as a parameter. We will also include the local state variables to this function.
const useFetchData = (url) => {
const [isLoading, setIsLoading] = useState(false);
const [apiData, setApiData] = useState(null);
const [serverError, setServerError] = useState(null);
};
the function described above will consists of useStates
as
-
isLoading
for checking if the API has fetched the data or is still fetching the data, -
apiData
: If data is fetched successfully then the data is stored inapiData
variable, -
serverError
: If there is any error in fetching the data from the API endpoint then we will store that error inserverError
variable.
- We are going to add an
useEffect
react hook. This hook will consists of anaxios.get(URL)
call on the requested URL.
const useFetch = (url) => {
const [isLoading, setIsLoading] = useState(false);
const [apiData, setApiData] = useState(null);
const [serverError, setServerError] = useState(null);
useEffect(() => {
setIsLoading(true);
const fetchData = async () => {
try {
const resp = await axios.get(url);
const data = await resp?.data;
setApiData(data);
setIsLoading(false);
} catch (error) {
setServerError(error);
setIsLoading(false);
}
};
fetchData();
}, [url]);
};
The axios
call is enclosed in a async function named fetchedData
. It consists of try...catch
block. Once the data is awaited we store it in apiData
using setApiData
. If you have observed I have also set isLoading
to true
at the start of the useEffect. This is done intentionally because we want to show the loader when the API has initiated a call to the server. Once we get the response with 200 status we set the isLoading
to false
using setIsLoading
.
If by some chance there is any error, we set the serverError
state to error
along with isLoading
state to false
.
- Finally we are going to return all the local state variables as an object.
const useFetch = (url) => {
const [isLoading, setIsLoading] = useState(false);
const [apiData, setApiData] = useState(null);
const [serverError, setServerError] = useState(null);
useEffect(() => {
setIsLoading(true);
const fetchData = async () => {
try {
const resp = await axios.get(url);
const data = await resp?.data;
setApiData(data);
setIsLoading(false);
} catch (error) {
setServerError(error);
setIsLoading(false);
}
};
fetchData();
}, [url]);
return { isLoading, apiData, serverError };
};
Usage in the actual component
Let us look at an example were we can use our custom hook useFetch
. Below is the code of the index.js
file.
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import useFetch from "./useFetch";
const App = () => {
const { isLoading, serverError, apiData } = useFetch(
"https://jsonplaceholder.typicode.com/posts/1"
);
return (
<div>
<h2>API data</h2>
{isLoading && <span>Loading.....</span>}
{!isLoading && serverError ? (
<span>Error in fetching data ...</span>
) : (
<span>{JSON.stringify(apiData)}</span>
)}
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<App />
</StrictMode>,
rootElement
);
Now just like any React hook we can directly use our custom hook to fetch the data. As you can see, isLoading
and serverError
can be used for conditional rendering of the component for displaying nice error messages.
Additional use cases
We can have additional use cases such as Adding adhoc redux actions, Generalizing the API methods etc. Below is the brief overview of the two use cases.
- Adding Adhoc redux actions: You can also integrate redux to your application and add an API response to your global state. Modification to the useFetch would look like this:
const useFetchData = (url) => {
const dispatch = useDispatch();
const [isLoading, setIsLoading] = useState(false);
const [apiData, setApiData] = useState(null);
const [serverError, setServerError] = useState(null);
useEffect(() => {
setIsLoading(true);
const fetchData = async () => {
try {
dispatch(fetchApiData());
const resp = await axios.get(url);
const data = await resp?.data;
dispatch(fetchApiSuccess(data));
setApiData(data);
setIsLoading(false);
} catch (error) {
setServerError(error);
dispatch(fetchApiFailure());
setIsLoading(false);
}
};
fetchData();
}, [dispatch, url]);
return { isLoading, apiData, serverError };
};
fetchApiData
, fetchApiSuccess
, and fetchApiFailure
are thunks which call the specific redux actions along with storing the data onto the redux global state.
- Generalizing API methods
Our
useFetch
is currently performing onlyGET
request. Ideal scenario is to have ouruseFetch
to perform all types of request such asPOST
,PUT
etc. Following code will make a generalized axios call.
const useFetch = (method, url, body) => {
const [isLoading, setIsLoading] = useState(false);
const [apiData, setApiData] = useState(null);
const [serverError, setServerError] = useState(null);
useEffect(() => {
setIsLoading(true);
const fetchData = async () => {
try {
const resp = await axios({
method: method,
url: url,
data: body
});
const data = await resp?.data;
setApiData(data);
setIsLoading(false);
} catch (error) {
setServerError(error);
setIsLoading(false);
}
};
fetchData();
}, [url, method, body]);
return { isLoading, apiData, serverError };
};
Usage will be the same as that of index.js. Only thing that is changing is the function definition:
const { isLoading, serverError, apiData } = useFetch(
"GET",
"https://jsonplaceholder.typicode.com/posts/1",
{}
);
These are some of the use cases which I think can most commonly used. You can you the above custom hook to fetch the data and enhance it as per our needs.
You can find the code used in this blogpost at the following sandbox URL:
https://codesandbox.io/s/react-custom-hook-sample-dcuf4
Feel free to reach out to me @
Top comments (7)
const { isLoading, serverError, apiData } = useFetch(
"https://jsonplaceholder.typicode.com/posts/1"
);
How can I append list (the apiData)? like concating with other list for infinite scrolling n all.
Would really like to know
Thanks
Nice Work!! 🔥
Good job mate, your article is very well presented.
Awesome post, it clarified a couple of doubts I had
ho do we use hook on onclick say when click button i need to fetch data
and how to we candle concurrent request
very good and informative article. Thanks very much for sharing