0

I am calling a single API many times in one of my React page depending on some data. The problem I am facing is to add a waiting loader until all APIs respond. I wrote the below code.

return function (dispatch) {
    document.body.classList.add('loading');
    let appName = payload.appName;
    let functionName = payload.functionName;
    let tokenKey = payload.tokenKey;
    let url = host + "/focusRestApi/webapi/fetchModelList";
    let config = { headers: { 'Authorization': "token " + tokenKey, 'Token': localStorage.getItem('token') } };
    let data = {};
    data.applicationName = appName;
    url = url + "?portfolioId=" + portfolioName;
    if(functionName.length!=0){
    for(let i=0;i<functionName.length;i++){
    data.functionality = functionName[i].applicationFunctionalityName;
    axios.post(url, data, config)
      .then((response) => {
        dispatch({
          type: "FUNCTIONVIEW_CTD_DATA", payload:
            { functionName: functionName[i].applicationFunctionalityName, data: response.data }
        });
        if(i === functionName.length-1) {
          document.body.classList.remove('loading');
        }
      });
    }

  }
}

Here, I have removed the loader inside an If condition. But it's not working fine. The loader is getting removed before all APis are responding. I am not sure what is the issue here. Can someone help me with this. Thanks in advance..

Updated: The above code has been written in getGithubModals function. And here is the code from a different JS file where I am calling the above function:

if (props.functionality_data_list.length === 0) {
        props.dispatch(getGithubModals({
          appName: appId,
          functionName: "0", githubUser: git_user, repo: git_repo, tokenKey: git_token
        }));
      } else {
        props.functionality_data_list.forEach(function (id) {
          functionName.push(id);
          props.dispatch(getFunctionalityModals({
            applicationName: appId,
            functionality: id.applicationFunctionalityName
          }));
        });
        props.dispatch(getGithubModals({
          appName: appId,
          functionName: functionName, githubUser: git_user, repo: git_repo, tokenKey: git_token
        }));
      }
  • Why are you calling same api multiple times? The code which you have written will remove the loader when your 1st api call resolves. – Utsav Patel Apr 21 '20 at 12:41
  • ohh..Actually in each api call I am getting different response depending upon the functionName that I am passing in one of the JS file..So there is a need to call same api a no. of times. Is there any other way that loader will not remove untill all APIs will respond.. –  Apr 21 '20 at 12:46
  • You would have to add a loader, whenever you initiate an api call. Although this will look bad i.e you would see loader adding and removing multiple times. Can you add the code where you make the api call? I mean where this function is used? – Utsav Patel Apr 21 '20 at 12:48
  • check out Promise.all(). you can pass it an array of promises and it resolves only after all the promises has resolved – Tom Slutsky Apr 21 '20 at 12:52
  • Hi @UtsavPatel, I have updated the code. Please check it out. –  Apr 21 '20 at 12:54
  • Does this answer your question? [Asynchronous Process inside a javascript for loop](https://stackoverflow.com/questions/11488014/asynchronous-process-inside-a-javascript-for-loop) – Dan O Apr 21 '20 at 12:58
  • there are many, many Stack Overflow questions regarding asynchronous JavaScript operations inside a loop. which of them have you researched and why specifically did they not solve your problem? – Dan O Apr 21 '20 at 12:59
  • I already tried many solutions @DanO. Nothing is solving my problem!! –  Apr 21 '20 at 13:07

1 Answers1

0

The following example will show a loader until an api call is completed.

In Actions File:

export const GET_SERVICE = '[SERVICE] GET LIST';
export const GET_SERVICE_SUCCESS = '[SERVICE] GET LIST SUCCESS';
export const GET_SERVICE_FAILURE = '[SERVICE] GET LIST FAILURE';

export function getService(options) {
    const request = axios.post('api/services', {options});
    return (dispatch) => {
        dispatch({
            type: GET_SERVICE
        });
        try {
            request.then((response) =>
                dispatch({
                    type: GET_SERVICE_SUCCESS,
                    payload: {data: response.data.data, meta: response.data.meta}
                })
            );
        } catch (e) {
            dispatch({
                type: GET_SERVICE_FAILURE
            });
        }
    }
}

In Reducer File:

const initialState = {
    services: [],
    loading: false,
    hasErrors: false,
};

const serviceReducer = function (state = initialState, action) {
    switch (action.type) {
        case Actions.GET_SERVICE:
            return {...state, loading: true};
        case Actions.GET_SERVICE_SUCCESS:
            return {
                ...state,
                loading: false,
                services: [...action.payload.data],
                page: action.payload.meta.page,
                total: action.payload.meta.total
            };
        case Actions.GET_SERVICE_FAILURE:
            return {...state, loading: false, hasErrors: true};
        default:
            return state;
    }
};

export default serviceReducer;

You can write a loader class like below:

import React from 'react'
import {ClipLoader} from "react-spinners";

const Loader = ({ loading, message }) => {

    return loading ? (
         <div className='wrapper'>
            <ClipLoader
               size={40}
               color={"#123abc"}
               loading={loading}
             />
             <span className='message'>
                {message}
             </span>
         </div>
    ) : null
};

export default Loader;

Now you can call the function getService in your App.js like below:

import React from "react";
import ReactDOM from 'react-dom'
import * as ServiceActions from "./store/actions";
import Loader from "./Loader";
import MUIDataTable from "mui-datatables";

class App extends React.Component {

    state = {
        services: null
    };

    callApi = () => {
         this.setState(getService());
    };

    render() {
        return (
            <div>
                <button onClick={this.callApi}>Load Data</button>
                {loading ? <Loader css='margin-left: 48%; margin-top: 10%' loading={loading}/> :
                    <MUIDataTable
                        title={"Service List"}
                        data={services}
                        columns={columns}
                        options={options}
                    />
                }

            </div>
        );
    }
}

ReactDOM.render(<App/>, document.getElementById("root"));

export default App;
Khabir
  • 5,370
  • 1
  • 21
  • 33