AlexW.H.B. AlexW.H.B. - 1 month ago 21
Javascript Question

How to add a loading indicator on every Relay.js network request

I'm currently building a Relay/React web app. It's been amazingly simple with one exception. I've not been able to figure out how I can get a global notification when any of my components are making network requests. I'm hopping to add a spinner to the top of my app when ever there's network activity, because some of my mutations take a long time to load.

This was my attempt at solving this problem, but it only works on new route page loads.

function renderer(info)
{
let {props, error, element} = info;
if (error) {
return (<ServerError errors={error}/>);
} else {
if (props) {
return React.cloneElement(element, props);
} else {
return (<Loading />);
}
}
}


ReactDOM.render(
<Router
history={browserHistory}
render={applyRouterMiddleware(useRelay)}
environment={Relay.Store}
>
<Route
path="/"
queries={ViewerQuery}
component={App}
>
<IndexRoute
queries={ViewerQuery}
component={Libraries}
render={renderer}
/>
<Route path="*" component={Error}/>

</Route>
</Router>


Ideally I could get some callback that I can pass to my App component, which renders all my pages headers and footers. Any help with this would be greatly appreciated. I've been all over the internet for a while trying to find a good solution to this.

Answer

You can create a custom spinner component in react and based on your data load status you can either show or hide the spinner.

An example for this can be -

Your Spinner component could be like this -

    let SpinMe
    = (
        <div className="spinner-container">
            <div className="loader">
                <svg className="circular">
                    <circle className     = "path"
                        cx                = "50"
                        cy                = "50"
                        r                 = "20"
                        fill              = "none"
                        strokeWidth      = "3"
                        strokeMiterLimit = "10"
                    />
                </svg>
            </div>
        </div>
    );

This spinner component should have somewhat higher z-index than other component so that while loading is occurring user can not click other components or interact with other components.

Also in styling show some transparent dark background of spinner.

e.g

.spinner-container {

    position         : absolute;
    background-color : rgba(12, 12, 12, 0.61);
    width            : 100%;
    min-height       : 100%;
    background-size  : 100% 100%;
    text-align       : center;
    z-index          : 3;
    top              : 0;
    left             : 0;
}

Now Your another component where you want to use the spinner and in this component you want to make network request.

import React from 'react';

class SpinTestComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
        isLoading:false
    };
  }

  sendNetworkRequest(URL, dataToSend) {
      return $.ajax({
            type        : 'POST',
            url         : URL,
            data        : JSON.stringify(dataToSend),
            dataType    : 'json'
            });
  }

  componentDidMount() {
        const URL = "test url";
        const dataToSend = { param1:"val", param2:"val" };

        this.setState({isLoading:true});
        this.sendNetworkRequest(dataToSend)
            .then(
            () => {
                // success response now remove spinner
                this.setState({isLoading:false});
            },
            () => {

                // error response again remove spinner and 
                // show error message to end user
                this.setState({isLoading:false});
            });
    }

    render() {
        return (
                <div>
                { this.state.isLoading ? <SpinMe/> : null }
                    <div>
                        <h1>
                            Remaining Component structure 
                            or Jsx
                        </h1>
                        <p>
                            To be show after loading is done.
                        </p>
                    </div>
                </div>
               );
    }
}

export default SpinTestComponent;