Rendering Loading Indicator

When the KendoReact Data Grid contains a huge amount of records and depending on the browser, the component might take longer to load its data.

In such cases, a loading indicator is suitable to indicate that the Grid is properly functioning and that its data will soon be displayed.

The following example demonstrates how to render a loading indicator once a request is made and hide it when the request is finished successfully.

import React from 'react';
import ReactDOM from 'react-dom';

import { Grid, GridColumn as Column } from '@progress/kendo-react-grid';
import { ProductsLoader } from './products-loader.jsx';

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            products: { data: [], total: 0 },
            dataState: { take: 10, skip: 0 }
        };
    }

    dataStateChange = (e) => {
        this.setState({
            ...this.state,
            dataState: e.data
        });
    }

    dataRecieved = (products) => {
        this.setState({
            ...this.state,
            products: products
        });
    }

    render() {
        return (
            <div>
                <Grid
                    filterable={true}
                    sortable={true}
                    pageable={true}
                    {...this.state.dataState}
                    {...this.state.products}
                    onDataStateChange={this.dataStateChange}
                >
                    <Column field="ProductID" filter="numeric" title="Id" />
                    <Column field="ProductName" title="Name" />
                    <Column field="UnitPrice" filter="numeric" format="{0:c}" title="Price" />
                    <Column field="UnitsInStock" filter="numeric" title="In stock" />
                </Grid>

                <ProductsLoader
                    dataState={this.state.dataState}
                    onDataRecieved={this.dataRecieved}
                />
            </div>
        );
    }
}

ReactDOM.render(<App />, document.querySelector('my-app'));

import React from 'react';
import ReactDOM from 'react-dom';
import { toODataString } from '@progress/kendo-data-query';

export class ProductsLoader extends React.Component {
    baseUrl = 'https://demos.telerik.com/kendo-ui/service-v4/odata/Products?$count=true&';
    init = { method: 'GET', accept: 'application/json', headers: {} };

    lastSuccess = '';
    pending = '';

    requestDataIfNeeded = () => {
        if (this.pending || toODataString(this.props.dataState) === this.lastSuccess) {
            return;
        }
        this.pending = toODataString(this.props.dataState);
        fetch(this.baseUrl + this.pending, this.init)
            .then(response => response.json())
            .then(json => {
                this.lastSuccess = this.pending;
                this.pending = '';
                if (toODataString(this.props.dataState) === this.lastSuccess) {
                    this.props.onDataRecieved.call(undefined, {
                        data: json.value,
                        total: json['@odata.count']
                    });
                } else {
                    this.requestDataIfNeeded();
                }
            });
    }

    render() {
        this.requestDataIfNeeded();
        return this.pending && <LoadingPanel />;
    }
}


class LoadingPanel extends React.Component {
    render() {
        const loadingPanel = (
            <div class="k-loading-mask">
                <span class="k-loading-text">Loading</span>
                <div class="k-loading-image"></div>
                <div class="k-loading-color"></div>
            </div>
        );

        const gridContent = document && document.querySelector('.k-grid-content');
        return gridContent ? ReactDOM.createPortal(loadingPanel, gridContent) : loadingPanel;
    }
}

Setup

  1. Create a component that will manage the data operations and the requests. This component will separate the data request and response logic from the declaration of the Grid.

    <Grid ...gridOptions></Grid>
       <ProductsLoader
           dataState={this.state.dataState}
           onDataRecieved={this.dataRecieved}
       />
    
       dataRecieved = (products) => {
           this.setState({
               ...this.state,
               products: products
           });
       }
  2. Inside the ProductLoader component, indicate to the Grid when to display the loading indicator. The time when the loading indicator will be rendered depends on the logic of the application.

    this.pending = toODataString(this.props.dataState);
       fetch(this.baseUrl + this.pending, this.init)
           .then(response => response.json())
           .then(json => {
               this.lastSuccess = this.pending;
               this.pending = '';
               if (toODataString(this.props.dataState) === this.lastSuccess) {
                   this.props.onDataRecieved.call(undefined, {
                       data: json.value,
                       total: json['@odata.count']
                   });
               } else {
                   this.requestDataIfNeeded();
               }
           });
       }
    render() {
           this.requestDataIfNeeded();
           return this.pending && <LoadingPanel />;
       }
  3. Create a component that will show the k-loading-mask over the Grid container.

    class LoadingPanel extends React.Component {
       render() {
           const loadingPanel = (
               <div class="k-loading-mask">
                   <span class="k-loading-text">Loading</span>
                   <div class="k-loading-image"></div>
                   <div class="k-loading-color"></div>
               </div>
           );
    
           const gridContent = document && document.querySelector('.k-grid-content');
           return gridContent ? ReactDOM.createPortal(loadingPanel, gridContent) : loadingPanel;
       }
      }

In this article