I'm using react context & axios interceptors to hide/show a loading spinner. While it does hide and show correctly when requests and responses are fired, my loading spinner is only showing at the start of a network request. The request fires and then goes into a 'pending' status. During the 'pending' status, the response interceptor is fired and hides the loading spinner.
How can I make sure the loading spinner stays visible during the pending requests?
I tried adding some console logs to fire when the requests, responses, and errors were returned including the count, and it showed it (for two requests) to successfully go from 0 - 1 - 2 - 1 - 0, but in the chrome devtools network tab showed as pending even though all requests were returned.
EDIT: thought I had it working after some refactor but it was a no-go. Added updated code
import React, { useReducer, useRef, useEffect, useCallback } from "react";
import { api } from "api/api";
import LoadingReducer from "reducer/LoadingReducer";
const LoadingContext = React.createContext();
export const LoadingProvider = ({ children }) => {
const [loader, dispatch] = useReducer(LoadingReducer, {
loading: false,
count: 0,
});
const loaderKeepAlive = useRef(null),
showLoader = useRef(null);
const showLoading = useCallback(() => {
dispatch({
type: "SHOW_LOADING",
});
}, [dispatch]);
const hideLoading = useCallback(() => {
loaderKeepAlive.current = setTimeout(() => {
dispatch({
type: "HIDE_LOADING",
});
}, 3000);
return clearTimeout(loaderKeepAlive.current);
}, [dispatch]);
const requestHandler = useCallback(
(request) => {
dispatch({ type: "SET_COUNT", count: 1 });
return Promise.resolve({ ...request });
},
[dispatch]
);
const errorHandler = useCallback(
(error) => {
dispatch({ type: "SET_COUNT", count: -1 });
return Promise.reject({ ...error });
},
[dispatch]
);
const successHandler = useCallback(
(response) => {
dispatch({ type: "SET_COUNT", count: -1 });
return Promise.resolve({ ...response });
},
[dispatch]
);
useEffect(() => {
if (loader.count === 0) {
hideLoading();
clearTimeout(showLoader.current);
} else {
showLoader.current = setTimeout(() => {
showLoading();
}, 1000);
}
}, [showLoader, showLoading, hideLoading, loader.count]);
useEffect(() => {
if (!api.interceptors.request.handlers[0]) {
api.interceptors.request.use(
(request) => requestHandler(request),
(error) => errorHandler(error)
);
}
if (!api.interceptors.response.handlers[0]) {
api.interceptors.response.use(
(response) => successHandler(response),
(error) => errorHandler(error)
);
}
return () => {
clearTimeout(showLoader.current);
};
}, [errorHandler, requestHandler, successHandler, showLoader]);
return (
<LoadingContext.Provider
value={{
loader,
}}
>
{children}
</LoadingContext.Provider>
);
};
export default LoadingContext;