Introduction
This is the 3rd article of the series "React.js with asp.net MVC application".In the previous post, I have shown you Displaying tabular data from database in react js Today I am going to show you server-side paging and sorting using React.js, ASP.NET MVC and entity framework.
In the previous article, we fetched all the data from a server at once which can be a performance issue fetching a large amount of data from server side at once. To resolve this performance issue, here in this article I will show you how to implement server-side paging and sorting in React JS.
Here We will fetch a list of employee data (JSON data) from a database via ajax and then we will create React components for displaying the employee data list in the tabular format with server-side paging and sorting.
Just follow the following steps in order to implement Datatables server-side paging, sorting and filtering in React JS.
Step - 1: Create New Project.
Go to File > New > Project > ASP.NET Web Application (under web) > Entry Application Name > Click OK > Select Empty template > Checked MVC (under "Add folders and core references for" option) > OKStep-2: Add a Database.
Go to Solution Explorer > Right Click on App_Data folder > Add > New item > Select SQL Server Database Under Data > Enter Database name > Add.Step-3: Create a table for store data.
Open Database > Right Click on Table > Add New Table > Add Columns > Save > Enter table name > Ok.Employee Table
Step-4: Add Entity Data Model.
Go to Solution Explorer > Right Click on Project name form Solution Explorer > Add > New item > Select ADO.net Entity Data Model under data > Enter model name > Add.A popup window will come (Entity Data Model Wizard) > Select Generate from database > Next >
Chose your data connection > select your database > next > Select tables > enter Model Namespace > Finish.
Step-5: Create an MVC Controller.
Go to Solution Explorer > Right Click on Controllers folder form Solution Explorer > Add > Controller > Enter Controller name > Select Templete "empty MVC Controller"> Add.Here I have created a controller named "HomeController"
Step-6: Add new action into your controller for getting the view, where we will implement our ReactJS component.
Here I have added "Index" Action into "Home" Controller. Please write this following codepublic ActionResult Index()
{
    return View();
}
Step-7: Add view for your Action and design.
Right Click on Action Method (here right click on Index action) > Add View... > Enter View Name > Select View Engine (Razor) > Add.HTML Code
@{
    ViewBag.Title = "Index";
}
<h2>Show tabular data from database in React JS with server-side paging and sorting</h2>
@* HTML *@
<div class="container" id="griddata">
    
</div>
@* CSS *@
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet" />
@* Jquery *@
<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
@* ReactJS library *@
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script>
@* JSX Parser *@
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
@* ReactJS component *@
<script type="text/babel">
    @* Here we will create React component *@
    @* 1. Paging component *@
    var GridPager = React.createClass({
        render : function(){
            var li = [];
            var pageCount = this.props.Size;
            for(var i = 1; i <=pageCount; i++){
                if(this.props.currentPage == i){
                    li.push(<li key={i} className="active"><a href="#">{i}</a></li>);
                }
                else{
                    li.push(<li key={i} ><a href="#" onClick={this.props.onPageChanged.bind(null,i)}>{i}</a></li>);
                }
            }
            return (<ul className="pagination">{li}</ul>);
        }
    });
   
    
    @* 2. Table row component *@
   var EmployeeGridRow = React.createClass({
        render : function(){
            return (
                <tr>
                    <td>{this.props.item.FirstName}</td>
                    <td>{this.props.item.LastName}</td>
                    <td>{this.props.item.EmailID}</td>
                    <td>{this.props.item.Country}</td>
                    <td>{this.props.item.City}</td>
                </tr>
            );
        }
    });
    @* 3. Table component *@
    var EmployeeGridTable = React.createClass({
        getInitialState : function(){
            return {
                Data : {
                    List : [],
                    totalPage : 0,
                    sortColumnName : null,
                    sortOrder : null,
                    currentPage : 1,
                    pageSize : 3
                }
            }
        },
        componentDidMount : function(){
            this.populateData();
        },
        @* function for populate data *@
        populateData: function(){
            var params = {
                pageSize : this.state.Data.pageSize,
                currentPage : this.state.Data.currentPage
            }
            if(this.state.Data.sortColumnName){
                params.sortColumnName = this.state.Data.sortColumnName;
            }
            if(this.state.Data.sortOrder){
                params.sortOrder = this.state.Data.sortOrder;
            }
            $.ajax({
                url : this.props.dataUrl,
                type : 'GET',
                data : params,
                success : function(data){
                    if(this.isMounted()){
                        this.setState({
                            Data : data
                        });
                    }
                }.bind(this),
                error: function(err){
                    alert('Error');
                }.bind(this)
            });
        },
        @* function for pagination *@
        pageChanged:function(pageNumber,e){
            e.preventDefault();
            this.state.Data.currentPage = pageNumber;
            this.populateData();
        },
        @* function for sorting *@
        sortChanged : function(sortColumnName, order , e){
            e.preventDefault();
            this.state.Data.sortColumnName = sortColumnName;
            this.state.Data.currentPage = 1;
            this.state.Data.sortOrder = order.toString().toLowerCase() == 'asc' ? 'desc':'asc';
            this.populateData();
        },
        @* function for set sort icon on table header *@
        _sortClass : function(filterName){
            return "fa fa-fw " + ((filterName == this.state.Data.sortColumnName) ? ("fa-sort-" + this.state.Data.sortOrder) : "fa-sort");
        },
        @* render *@
        render : function(){
            var rows = [];
            this.state.Data.List.forEach(function(item){
                rows.push(<EmployeeGridRow key={item.EmployeeID} item={item}/>);
            });
            return (
                <div>
                    <table className="table table-responsive table-bordered">
                        <thead>
                            <tr>
                                <th onClick={this.sortChanged.bind(this,'FirstName',this.state.Data.sortOrder)}>First Name
                                    <i className={this._sortClass('FirstName')}></i></th>
                                <th onClick={this.sortChanged.bind(this,'LastName',this.state.Data.sortOrder)}>
                                    Last Name
                                    <i className={this._sortClass('LastName')}></i></th>
                                <th onClick={this.sortChanged.bind(this,'EmailID',this.state.Data.sortOrder)}>
                                    Email
                                    <i className={this._sortClass('EmailID')}></i>
                                </th>
                                <th onClick={this.sortChanged.bind(this,'Country',this.state.Data.sortOrder)}>
                                    Country
                                    <i className={this._sortClass('Country')}></i>
                                </th>
                                <th onClick={this.sortChanged.bind(this,'City',this.state.Data.sortOrder)}>
                                    City
                                    <i className={this._sortClass('City')}></i>
                                </th>
                            </tr>
                        </thead>
                        <tbody>{rows}</tbody>
                    </table>
                    <GridPager Size={this.state.Data.totalPage} onPageChanged={this.pageChanged} currentPage={this.state.Data.currentPage}/>
                </div>
            );
        }
    });
    ReactDOM.render(<EmployeeGridTable dataUrl="/home/getEmployeeList"/>, document.getElementById('griddata'));
</script>
<style>
    #griddata th
    {
        cursor: pointer;
    }
    .fa
    {
        float: right;
        margin-top: 4px;
    }
</style>
Also, I have added functions populateData (for fetching data from the server), pageChanged (paging functionality), sortChanged (for sorting event) and _sortClass (for adding sort icon on table header column).
Step-8: Add reference of System.Linq.Dynamic
Here I have added System.Linq.Dynamic reference from NuGet packagesGo to Solution Explorer > Right click on References > Manage NuGet packages > Search with "System.Linq.Dynamic" > Install.
Step-9: Add another action to your controller for return data(employee) list as JSON Data
Here I have used "GetEmployeeData" Action for fetch data. Please write this following codepublic JsonResult getEmployeeList(string sortColumnName = "FirstName", string sortOrder = "asc", int pageSize=3, int currentPage=1)
{
    List<Employee> List = new List<Employee>();
    int totalPage = 0;
    int totalRecord = 0;
    using (MyDatabaseEntities dc = new MyDatabaseEntities())
    {
        var emp = dc.Employees;
        totalRecord = emp.Count();
        if (pageSize > 0)
        {
            totalPage = totalRecord / pageSize + ((totalRecord % pageSize) > 0 ? 1 : 0);
            List = emp.OrderBy(sortColumnName + " " + sortOrder).Skip(pageSize * (currentPage - 1)).Take(pageSize).ToList();
        }
        else
        {
            List = emp.ToList();
        }
    }
    return new JsonResult
    {
        //Data = new { List = List, totalPage = totalPage, sortColumnName = sortColumnName, sortOrder = sortOrder, currentPage = currentPage},
        Data = new { List = List, totalPage = totalPage, sortColumnName = sortColumnName, sortOrder = sortOrder, currentPage = currentPage, pageSize = pageSize },
        JsonRequestBehavior = JsonRequestBehavior.AllowGet
    };
}
Step-10: Run Application.
@{
    ViewBag.Title = "Index";
}
<h2>Show tabular data from database in React JS with server-side paging and sorting</h2>
@* HTML *@
<div class="container" id="griddata">
    
</div>
@* CSS *@
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet" />
@* Jquery *@
<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
@* ReactJS library *@
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script>
@* JSX Parser *@
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
@* ReactJS component *@
<script type="text/babel">
    @* Here we will create React component *@
    @* New : Search box component *@
    var SearchBox = React.createClass({
        handleChange : function(e){
            console.log('search');
            this.props.onSearchChanged(e.target.value);
        },
        render: function(){
            return (
                <div style={{marginBottom:15+'px',float:'right'}} ><input type="text" value={this.props.searchText} placeholder="search" onChange={this.handleChange}/></div>
            )
        }
    });
    @* 1. Paging component *@
    var GridPager = React.createClass({
        render : function(){
            var li = [];
            var pageCount = this.props.Size;
            for(var i = 1; i <=pageCount; i++){
                if(this.props.currentPage == i){
                    li.push(<li key={i} className="active"><a href="#">{i}</a></li>);
                }
                else{
                    li.push(<li key={i} ><a href="#" onClick={this.props.onPageChanged.bind(null,i)}>{i}</a></li>);
                }
            }
            return (<ul className="pagination">{li}</ul>);
        }
    });
   
    
    @* 2. Table row component *@
   var EmployeeGridRow = React.createClass({
        render : function(){
            return (
                <tr>
                    <td>{this.props.item.FirstName}</td>
                    <td>{this.props.item.LastName}</td>
                    <td>{this.props.item.EmailID}</td>
                    <td>{this.props.item.Country}</td>
                    <td>{this.props.item.City}</td>
                </tr>
            );
        }
    });
    @* 3. Table component *@
    var EmployeeGridTable = React.createClass({
        getInitialState : function(){
            return {
                Data : {
                    List : [],
                    totalPage : 0,
                    sortColumnName : null,
                    sortOrder : null,
                    currentPage : 1,
                    pageSize : 3,
                    searchText:''
                }
            }
        },
        componentDidMount : function(){
            this.populateData();
        },
        @* function for populate data *@
        populateData: function(){
            var params = {
                pageSize : this.state.Data.pageSize,
                currentPage : this.state.Data.currentPage,
                searchText : this.state.Data.searchText
            }
            if(this.state.Data.sortColumnName){
                params.sortColumnName = this.state.Data.sortColumnName;
            }
            if(this.state.Data.sortOrder){
                params.sortOrder = this.state.Data.sortOrder;
            }
            $.ajax({
                url : this.props.dataUrl,
                type : 'GET',
                data : params,
                success : function(data){
                    if(this.isMounted()){
                        this.setState({
                            Data : data
                        });
                    }
                }.bind(this),
                error: function(err){
                    alert('Error');
                }.bind(this)
            });
        },
        @* function for pagination *@
        pageChanged:function(pageNumber,e){
            e.preventDefault();
            this.state.Data.currentPage = pageNumber;
            this.populateData();
        },
        @* function for sorting *@
        sortChanged : function(sortColumnName, order , e){
            e.preventDefault();
            this.state.Data.sortColumnName = sortColumnName;
            this.state.Data.currentPage = 1;
            this.state.Data.sortOrder = order.toString().toLowerCase() == 'asc' ? 'desc':'asc';
            this.populateData();
        },
        searchChange : function(value){
            var d = this.state.Data;            
            d.searchText = value;
            this.setState({
                Data : d            
            });
            this.populateData();
        },
        @* function for set sort icon on table header *@
        _sortClass : function(filterName){
            return "fa fa-fw " + ((filterName == this.state.Data.sortColumnName) ? ("fa-sort-" + this.state.Data.sortOrder) : "fa-sort");
        },
        @* render *@
        render : function(){
            var rows = [];
            this.state.Data.List.forEach(function(item){
                rows.push(<EmployeeGridRow key={item.EmployeeID} item={item}/>);
            });
            return (
                <div>
                    <SearchBox onSearchChanged={this.searchChange} searchText={this.state.Data.searchText}/>
                    <table className="table table-responsive table-bordered">
                        <thead>
                            <tr>
                                <th onClick={this.sortChanged.bind(this,'FirstName',this.state.Data.sortOrder)}>First Name
                                    <i className={this._sortClass('FirstName')}></i></th>
                                <th onClick={this.sortChanged.bind(this,'LastName',this.state.Data.sortOrder)}>
                                    Last Name
                                    <i className={this._sortClass('LastName')}></i></th>
                                <th onClick={this.sortChanged.bind(this,'EmailID',this.state.Data.sortOrder)}>
                                    Email
                                    <i className={this._sortClass('EmailID')}></i>
                                </th>
                                <th onClick={this.sortChanged.bind(this,'Country',this.state.Data.sortOrder)}>
                                    Country
                                    <i className={this._sortClass('Country')}></i>
                                </th>
                                <th onClick={this.sortChanged.bind(this,'City',this.state.Data.sortOrder)}>
                                    City
                                    <i className={this._sortClass('City')}></i>
                                </th>
                            </tr>
                        </thead>
                        <tbody>{rows}</tbody>
                    </table>
                    <GridPager Size={this.state.Data.totalPage} onPageChanged={this.pageChanged} currentPage={this.state.Data.currentPage}/>
                </div>
            );
        }
    });
    ReactDOM.render(<EmployeeGridTable dataUrl="/home/getEmployeeList"/>, document.getElementById('griddata'));
</script>
Updated step-9
public JsonResult getEmployeeList(string sortColumnName = "FirstName", string sortOrder = "asc", int pageSize = 3, int currentPage = 1, string searchText = "")
{
    List<Employee> List = new List<Employee>();
    int totalPage = 0;
    int totalRecord = 0;
    using (MyDatabaseEntities dc = new MyDatabaseEntities())
    {
        var emp = dc.Employees.Select(a => a);
        //Search
        if (!string.IsNullOrEmpty(searchText))
        {
            emp = emp.Where(a => a.FirstName.Contains(searchText) || a.LastName.Contains(searchText) || a.EmailID.Contains(searchText) || a.City.Contains(searchText) || a.Country.Contains(searchText));
        }
        totalRecord = emp.Count();
        if (pageSize > 0)
        {
            totalPage = totalRecord / pageSize + ((totalRecord % pageSize) > 0 ? 1 : 0);
            List = emp.OrderBy(sortColumnName + " " + sortOrder).Skip(pageSize * (currentPage - 1)).Take(pageSize).ToList();
        }
        else
        {
            List = emp.ToList();
        }
    }
    return new JsonResult
    {
        //Data = new { List = List, totalPage = totalPage, sortColumnName = sortColumnName, sortOrder = sortOrder, currentPage = currentPage},
        Data = new { List = List, totalPage = totalPage, sortColumnName = sortColumnName, sortOrder = sortOrder, currentPage = currentPage, pageSize = pageSize, searchText = searchText },
        JsonRequestBehavior = JsonRequestBehavior.AllowGet
    };
}
Live Demo Download