I would like to add a Loader component to be rendered whenever an API call is being made in React. I want to use react context + hooks instead of redux.
As the rules of react hooks say, we should not use react hooks outside the react component. But I need to dispatch the SHOW_LOADER and HIDE_LOADER inside the Axios interceptor as below.
Is there a way to achieve this?
import axios from "axios";
axios.interceptors.request.use(
config => {
dispatch({
type: "SHOW_LOADER"
})
return config;
},
error => {
dispatch({
type: "HIDE_LOADER"
})
return Promise.reject(error);
}
);
axios.interceptors.response.use(
response => {
dispatch({
type: "HIDE_LOADER"
})
return response;
},
error => {
dispatch({
type: "HIDE_LOADER"
})
return Promise.reject(error);
}
);
function GlobalLoader(){
const [state,dispatch] = useContext(LoaderContext);
return(
<div>
{
state.loadStatus &&
<Loader
type = "Puff"
color = "#00BFFF"
height = {100}
width = {100}
timeout = {3000} />
}
</div>
);
}
export default GlobalLoader;
Please let me know if more information is required.:)
You can define a loading state in the reducer with "SHOW_LOADER" & "HIDE_LOADER" action types. These interceptors will dispatch the corresponding actions before a request is sent and received via axios, updating the loading state in store through which you can render a Loader component. Hope this answers your question.
import axios from 'axios'; const instance = axios. create({ baseURL: "https://example.com" }) export default instance; As we need React component to use the hooks, let's write a component here. Adding an interceptor in a component is a side effect, so we get help from useEffect hook.
js with the following code: import React from 'react'; import spinner from './spinner. gif'; function Spinner() { return ( <div> <img src={spinner} style={{ width: '100px', margin: 'auto', display: 'block' }} alt="Loading..." /> </div> ); }; export default Spinner; Here, we import the .
Create an axios instance using axios.create(config). Use this instance inside useEffect() to add interceptors that can effect the state (reducer is an overkill here). Now use the instance everywhere, and the interceptors will cause a change in the state.
Note: Since multiple requests can start/and or end, you should use a counter. Increment on request, and decrement on response. If the counter is not 0, the application is loading.
const { useState, useMemo, useEffect } = React;
const ax = axios.create(); // export this and use it in all your components
const useAxiosLoader = () => {
const [counter, setCounter] = useState(0);
const interceptors = useMemo(() => {
const inc = () => setCounter(counter => counter + 1);
const dec = () => setCounter(counter => counter - 1);
return ({
request: config => (inc(), config),
response: response => (dec(), response),
error: error => (dec(), Promise.reject(error)),
});
}, []); // create the interceptors
useEffect(() => {
// add request interceptors
const reqInterceptor = ax.interceptors.request.use(interceptors.request, interceptors.error);
// add response interceptors
const resInterceptor = ax.interceptors.response.use(interceptors.response, interceptors.error);
return () => {
// remove all intercepts when done
ax.interceptors.request.eject(reqInterceptor);
ax.interceptors.response.eject(resInterceptor);
};
}, [interceptors]);
return [counter > 0];
};
const GlobalLoader = () => {
const [loading] = useAxiosLoader();
return(
<div>
{
loading ? 'loading' : 'not loading'
}
</div>
);
}
const callApi = (err) => ax.get(err ? 'https://asdf' : 'https://www.boredapi.com/api/activity')
// make a request by using the axios instance
setTimeout(() => {
callApi();
callApi(true);
callApi();
}, 1000);
ReactDOM.render(
<GlobalLoader />,
root
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.js"></script>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With