Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Hooks: Display global spinner using axios interceptor?

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.:)

like image 805
CodeZombie Avatar asked Dec 14 '19 14:12

CodeZombie


People also ask

How do you add global loading spin effect in Axios interceptor for a React project?

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.

How do you use Axios interceptor hooks?

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.

How do you implement loading spinner in React?

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 .


1 Answers

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>
like image 72
Ori Drori Avatar answered Oct 14 '22 01:10

Ori Drori