Diego Ferreira
Diego Ferreira

Reputation: 21

Show Loading Spinner with Axios and React (possible Redux)

My objective is show a loading spinner when a http request is called. I want to do this globally, It means, when any component call some api, is automatic to show teh spinner, preferably, with no aditional code to control the spinner.

I'm using react and axios. I know about the axios' interceptors.

My actual strucuture:

<Component App>
    <Component A />
    <Component B />
    <Loading Component>
</Component App>

I don't know is It possible in react, but I Tried to make a general class to implement a base informations about the service, for example, baseUrl and the http interceptor. So, when a need to call a API service, I create a specialized class using the general class and concentred all api methods from that concept there, and, then, use the base class to call the axios method get,post,etc. So, the interceptor appears and show the spinner before the request and hide after the request.

The idea is a component call the specialized class to call a API and show the spinner until the request is running.

I think to use redux to make the state be global, but I don't know if It possible to implement in a class (API Class Base and Specialized API Class)

My questions: - Is It possible use this architeture? - If yes, how to implement and use redux with this? - IIs that right to use class or is better use a component to implement a base service class and the specialized class?

App component:

class App extends Component {

  constructor(props) {
      super(props);

      this.state = {
          rows: []
      };
  }

  componentDidMount() {
var service = new specializedService();

     var response = 

 specializedService.someAPIMethod(1).then((res) => {
           this.setState({
               rows: res.data
           });
     });
}

render() {
return (
        <div className="App">
                <Component A rows={this.state.rows} />
                <Component B rows={this.state.rows}/>
                <Loading />
        </div>
    );
  }

specializedService:

import serviceBase from "./serviceBase";

class specializedService {

    someAPIMethod(id) {
        return serviceBase.get('someMethod/' + id);
    };

}

serviceBase:

import axios from "axios";

const serviceBase = () => {

    const api = axios.create({
        baseURL: 'https://xxxxx.xxxxx.com/xxxxxx/'
    });

    api.interceptors.request.use(async (config) => {
        try{
            // In this moment, show the spinner
            // showLoading();
        }
        catch(e)
        {
            alert('Error request' + e);
        }

        return config;
    });

    api.interceptors.response.use(async (config) => {
        try {
            // In this moment, hide the spinner
            // hideLoading();
        }
        catch(e)
        {
            alert('Error response' + e);   
        }

        return config;
    });

    return api;
};

export default serviceBase;

I made a simple sample to illustrate the situation, but I don't connect the redux https://repl.it/@DiegoFerreira1/LoadingSpinnerApp

Upvotes: 1

Views: 23589

Answers (4)

K.A
K.A

Reputation: 1659

you can do it by set new key inside your state , for example "loading: true" and after you API call is done change it to a false , here is a simple example for a 'Blog' component ,

note : just focus on "class Blog component":


import { BrowserRouter, Route, Routes ,
        NavLink 
        } from "react-router-dom";

import './App.css';
import React, { Component } from 'react';
import axios from 'axios';

function App() {
  return (
    <div className="App">
        <h1>hello from react</h1>
        <BrowserRouter>
            <Nav />
            <Routes>
                <Route  path="/"   element={<Home />}/>
                <Route  path="/blog" element={<Blog />}/>

            </Routes>

        </BrowserRouter>
    </div>
  );
}

export default App;


function Nav() {
    return (
      <div className="App">
        <h1> 
            <NavLink to='/'> home</NavLink>  -
            <NavLink to='/about'> about</NavLink> -
            <NavLink to='/blog'> Blog</NavLink>
            
        </h1>
      </div>
    );
  }

function Home() {
    return (
      <div className="App">
          <h1>hello from Home</h1>
      </div>
    );
  }


class Blog extends Component {
    state = {
        users: [],
        loading: true
    }

    componentDidMount(){
        axios.get('https://jsonplaceholder.typicode.com/users')
        .then(res => {
            console.log(res.data)
            
            this.setState({
                users: res.data,
                loading: false

            })
        })
    }

    render(){
        return(
            <div> 
                <h1>Blog Date</h1>
                {
                    this.state.loading === true ? 
                    <div > Loading... </div> :
                     <div > 
                         {this.state.users.map( user => <div key={user.id}> {user.name} </div>)}
                     </div>

                }


            </div>
        )
    }
  }

  

this is just an example , and change it based on your case/requirements.

Upvotes: 0

pope_maverick
pope_maverick

Reputation: 980

this is very handy using redux and thunk.

but, you can use a hack like,

this.state = {
 rows: [],
 isLoading: false,
}

apiCallFunction = () => {

 this.setState(prevState => ({ isLoading: !prevState.isLoading }) // to be true..

 // api call..
 // upon response success => 
 this.setState(prevState => ({
   isLoading: !prevState.isLoading, // to be false..
   rows: prevState.rows = response json,
 })
}

and in render,

render () {
 return (
  {isLoading && <Spinner />}
  {... handle the rest} 
 )
}

Upvotes: 1

Dor Shinar
Dor Shinar

Reputation: 1512

I think the best way to achieve that would be using redux. you can have a "request status" reducer of some sort, and a value in your store that indicates whether a request is being sent.

Pseudo-code:

const sendRequest = async (req) {
  dispatch({type: "REQUEST_SENT"});
  const res = await axios.get(req);
  dispatch({type: "REQUEST_SUCCEEDED", payload: res});
}

And depending on the action you switch the value of your request status in your store, re-rendering your components.

Upvotes: 0

blonial
blonial

Reputation: 81

You can try use react context and share method which will change visibility of your Loading component.

Upvotes: 0

Related Questions