简体   繁体   中英

Intercept all API requests with a Loader Component - React Axios

I would like to add a Loader component to be rendered whenever an API call is being made in React. I have an isFetching flag in my reducer and now I am able to do it on a component level based on this prop. But I need one globally. Is there a way to achieve this?

This is the implementation I am planning to use.

Below is my dataService:

        //if dev mode them URL prefix = http://127.0.0.1:8080/
    //else
    // prefix= .

    // url= prefix + /users/xyz
    import axios from 'axios';

    const DEV_MODE_URL_PREFIX = '/api/';
    const PROD_MODE_URL_PREFIX = './api/';
    const ENV = process.env.NODE_ENV;

    function getService(relativeUrl, options) {

        return dataService(relativeUrl, options);
    }

    function postService(relativeUrl, dataToPost, options) {
        return dataService(relativeUrl, options, dataToPost);
    }

    function dataService(relativeUrl, options, dataToPost) {
        /**
         * dataToPost = {
         *  key: value
         * } || string
         * options = {
         *  actions: {
         *    requestAction: function ()
         *    successAction: function (response)
         *    failureAction: function (error)
         *  },
         *  shouldRequest: function (state)
         * }
         */
        return (dispatch, getState) => {
            let url;
            const { requestAction, successAction, failureAction } = options.actions;
            if (relativeUrl.match(/json/)) {
                url = relativeUrl;
            } else {
                if (relativeUrl[0] === '/') {
                    relativeUrl = relativeUrl.slice(1);
                }
                if (relativeUrl[relativeUrl.length - 1] !== '/') {
                    relativeUrl += '/';
                }
                if (ENV !== 'production') {
                    url = DEV_MODE_URL_PREFIX + relativeUrl;
                } else {
                    url = PROD_MODE_URL_PREFIX + relativeUrl;
                }
            }

            if (options.shouldRequest(getState())) {
                let promise;
                const data = typeof dataToPost === 'string' ? { 'data': dataToPost } : dataToPost;
                dispatch(requestAction());
                promise = dataToPost ? axios.post(url, data, { withCredentials: true }) : axios.get(url, { withCredentials: true });
                return promise
                    .then(response => {
                        console.log(
                            `status: ${response.status}
                message: ${response.message}`
                        );
                        if (response.status === 200) {
                            return dispatch(successAction(response, dispatch));
                        }
                        return Promise.reject(response);
                    })
                    .catch(error => {
                        console.log(error);
                        return dispatch(failureAction(error));
                    });
            } else {
                return Promise.reject('FETCHING');
            }
        };
    }

    export {
        getService,
        postService
    };

Please advice.

I have earlir done it in Angular and would like the same thing in React.

You can setup axios interceptors in a higher level component let's say App. 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.

App Component

import React from 'react';
import axios from 'axios'
import { connect } from 'react-redux';
import { loading } from '../actions'
import Loader from './Loader'

class App extends React.Component{
 componentWillMount(){
   const self = this
   axios.interceptors.request.use(function (config) {
     // spinning start to show
     self.props.loading(true)
     return config
    }, function (error) {
      return Promise.reject(error);
    });

    axios.interceptors.response.use(function (response) {
     // spinning hide
      self.props.loading(false)

     return response;
   }, function (error) {
     return Promise.reject(error);
   });
 }
 render(){
   return (
    <div>
      { this.props.loader ? <Loader /> : null }

      {/* 
        Your other components
      */}
    </div>
   )
 }

}

const mapStateToProps = (state)=>{
  return {
    loader: state.loader
  }
}

export default connect(mapStateToProps,{
  loading
})(App);

Loader Reducer

const loader = (state = false, action) => {
    switch (action.type) {
        case "SHOW_LOADER":
            return action.data;
            break;
        case "HIDE_LOADER":
            return action.data;
            break;
        default:
            return state;
    }
}

export default loader;

Action

export const loading = (bool)=>{
  return bool ? {
    type:"SHOW_LOADER",
    data:bool
  }: {
    type: "HIDE_LOADER",
    data: bool
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM