Remote Data in React Grid component

9 May 202424 minutes to read

In React Grid component, binding remote data is a fundamental aspect that enhances the efficiency of data interaction. This process involves assigning the service data, represented as an instance of DataManager, to the dataSource property of the React Grid component. By doing so, you enable seamless interaction with a remote data source, and this is achieved by specifying the endpoint URL where the data is hosted.

Additionally, leverage the power for data retrieval and operations, enhancing event handling, asynchronous programming, and concurrent value management in React applications.

Custom binding

The custom binding feature in the React Grid enables you to manage your own custom API for handling data processing externally and then binding the resulting data to the Grid. This allows you to implement your own custom data logic to your application’s requirements. When using custom binding, the Grid expects the result of the custom logic to be an object with properties result and count. The result property should contain the data to be displayed in the Grid, while the count property indicates the total number of records in the dataset for your application. To utilize custom binding, you can handle the DataManager. The DataManager integrates seamlessly with the React Grid to manage custom data processing and binding.

The Syncfusion Grid component offers a range of powerful features for handling grid actions such as paging, grouping, sorting and filtering. These actions trigger the dataStateChange event. The feature for CRUD action such as Create, Read, Update, Delete operations. This action trigger the dataSourceChanged event. This event provides you with the opportunity to manage and manipulate data according to the individual’s interactions.

Using the dataStateChange event

The dataStateChange event is triggered whenever you perform actions that modify the state of the grid’s data, such as changing pages, applying sorting, or grouping. This event provides detailed information about the action performed and the current state of the grid, including parameters like page number, sorting details, and filtering criteria.

To implement the dataStateChange event, follow these steps:

  1. Subscribe to the event: In your component code, subscribe to the dataStateChange event using the appropriate event handler function. This function will be executed whenever you interact with the grid.

  2. Handle data state: Inside the event handler function, you can access the event arguments to determine the individual actions and intentions. The action property of the event arguments indicates the type of action performed (e.g., paging, sorting, grouping).

The dataStateChange event will not be triggered during the initial rendering.

Handling filtering operation

When filtering operation is performed in the grid, the dataStateChange event is triggered, providing access to the following referenced arguments within the event.

FilterBar

You can change the new grid data state of filter action as follows:

const applyFiltering = (query: Query, filter: any) => {
    if (filter.columns && filter.columns.length) {
      for (let i = 0; i < filter.columns.length; i++) {
        const field = filter.columns[i].field;
        const operator = filter.columns[i].operator;
        const value = filter.columns[i].value;
        query.where(field, operator, value);
      }
    }
    else {
      for (let i = 0; i < filter.length; i++) {
        const { fn, e } = filter[i];
        if (fn === 'onWhere') {
          query.where(e as string);
        }
      }
    }
  }
/** GET all data from the server */
const reducer = (state = initialState, action) => {
  createLazyLoadData();
  const dataSource = [...initialState.data];
  const query = new Query();
  switch (action.type) {
    case Grid_FetchData: {
      if (action.payload.where) {
        applyFiltering(query, action.gridQuery.queries);
        // To get the count of the data
        query.isCountRequired = true;
        // Execute local data operations using the provided query
        const currentResult = new DataManager(dataSource).executeLocal(query);
        // Return the result along with the count of total records
        return ({
            data: {
                result: currentResult.result, // Result of the data
                count: currentResult.count // Total record count
            } 
        })
      }
    }
  }
}

Filtering Multiple Values

When filtering multiple values, you can get the predicates as arguments in the dataStateChange event. You can create your predicate execution based on the predicates values.

Handling searching operation

When performing a search operation in the grid, the dataStateChange event is triggered, allowing access to the following referenced arguments within the event

Searching

You can change the new grid data state of search action as follows:

const applySearching = (query: Query, search: any) => {
  // Check if a search operation is requested
  if (search && search.length > 0) {
    // Extract the search key and fields from the search array
    const { fields, key } = search[0];
    // perform search operation using the field and key on the query
    query.search(key, fields);
  }
}
/** GET all data from the server */
const reducer = (state = initialState, action: any) => {
  createLazyLoadData();
  const dataSource = [...initialState.data];
  const query = new Query();
  switch (action.type) {
    case Grid_FetchData: {
      if (!isNullOrUndefined(action.payload.search)) {
        applySearching(query, action.payload.search);
      }
      // To get the count of the data
      query.isCountRequired = true;
      // Execute local data operations using the provided query
      const currentResult = new DataManager(dataSource).executeLocal(query);
      // Return the result along with the count of total records
      return ({
          data: {
              result: currentResult.result, // Result of the data
              count: currentResult.count // Total record count
          } 
      })
    }
  }
}

Handling sorting operation

When sorting operation is performed in the grid, the dataStateChange event is triggered, and within this event, you can access the following referenced arguments.

Sorting

When performing multi-column sorting, you can get the below referred arguments in the dataStateChange event.

Multi Sorting

You can change the new grid data state of sort action as follows:

const applySorting = (query: Query, sorted: sortInfo[]) => {
  if (sorted && sorted.length > 0) {
    sorted.forEach(sort => {
      const sortField = sort.name || sort.field;
      query.sortBy(sortField as string, sort.direction);
    });
  }
}
/** GET all data from the server */
const reducer = (state = initialState, action) => {
    createLazyLoadData();
    const dataSource = [...initialState.data];
    const query = new Query();
    switch (action.type) {
      case Grid_FetchData: {
        if (!isNullOrUndefined(action.payload.sorted)) {
            applySorting(query, action.payload.sorted);
        }
        // To get the count of the data
        query.isCountRequired = true;
        // Execute local data operations using the provided query
        const currentResult = new DataManager(dataSource).executeLocal(query);
        // Return the result along with the count of total records
        return ({
            data: {
                result: currentResult.result, // Result of the data
                count: currentResult.count // Total record count
            } 
        })
      }
    }
}

Handling paging operation

When paging operation is performed in the grid, the dataStateChange event is triggered, and within this event, you can access the following referenced arguments.

Paging

You can change the new grid data state of page action as follows:

const applyPaging = (query, page) => {
  // Check if both 'take' and 'skip' values are available
  if (page.take && page.skip) {
    // Calculate pageSkip and pageTake values to get pageIndex and pageSize
    const pageSkip = page.skip / page.take + 1;
    const pageTake = page.take;
    query.page(pageSkip, pageTake);
  }
  // If if only 'take' is available and 'skip' is 0, apply paging for the first page.
  else if (page.skip === 0 && page.take) {
    query.page(1, page.take);
  }
}
/** GET all data from the server */
const reducer = (state = initialState, action) => {
  createLazyLoadData();
  const dataSource = [...initialState.data];
  const query = new Query();
  switch (action.type) {
    case Grid_FetchData: {
      applyPaging(query, action.payload);
      // To get the count of the data
      query.isCountRequired = true;
      // Execute local data operations using the provided query
      const currentResult = new DataManager(dataSource).executeLocal(query);
      // Return the result along with the count of total records
      return ({
          data: {
              result: currentResult.result, // Result of the data
              count: currentResult.count // Total record count
          } 
      })
    }
  }
}

Handling grouping operation

When grouping operation is performed in the grid, the dataStateChange event is triggered, providing access to the following referenced arguments within the event.

Grouping

You can change the new grid data state of group action as follows:

const applyGrouping = (query: Query, group: Object[]) => {
  // Check if grouping data is available
  if (group && group.length > 0) {
    for (let i = 0; i < group.length; i++) {
      // perform group operation using the column on the query
      query.group(group[i] as string);
    }
  }
}
/** GET all data from the server */
const reducer = (state = initialState, action) => {
  createLazyLoadData();
  const dataSource = [...initialState.data];
  const query = new Query();
  switch (action.type) {
    case Grid_FetchData: {
      if (!isNullOrUndefined(action.payload.group)) {
        applyGrouping(query, action.payload.group);
      }
      // To get the count of the data
      query.isCountRequired = true;
      // Execute local data operations using the provided query
      const currentResult = new DataManager(dataSource).executeLocal(query);
      // Return the result along with the count of total records
      return ({
          data: {
              result: currentResult.result, // Result of the data
              count: currentResult.count // Total record count
          } 
      })
    }
  }
}
  • In order to utilize group actions, it is necessary to manage the sorting query within your service.

Lazy load grouping

In React, lazy loading refers to the technique of loading data dynamically when they are needed, instead of loading everything upfront. Lazy load grouping allows you to load and display grouped data efficiently by fetching only the required data on demand.

To enable this feature, you need to set the groupSettings.enableLazyLoading property to true. Also, you need to manage the state based on the initial grid action as follows.

const groupOptions = { columns: ['ProductName'], enableLazyLoading: true };
const state = { skip:0, take: 12, group: groupOptions};

Based on the initial state, you can get the arguments as shown below

Lazy load group

You can change the grid state as follows:

const applyGrouping = (query: Query, group: Object[]) => {
  // Check if grouping data is available
  if (group && group.length > 0) {
    for (let i = 0; i < group.length; i++) {
      // perform group operation using the column on the query
      query.group(group[i] as string);
    }
  }
}
const applyLazyLoad = (query: Query, payload: payloadInfo) => {
  // Configure lazy loading for the main data
  if (payload.isLazyLoad) {
    query.lazyLoad.push({ key: 'isLazyLoad', value: true });
    // If on-demand group loading is enabled, configure lazy loading for grouped data
    if (payload.onDemandGroupInfo) {
      query.lazyLoad.push({
          key: 'onDemandGroupInfo',
          value: payload.action.lazyLoadQuery,
      });
    }
  }
}
/** GET all data from the server */
const reducer = (state = initialState, action) => {
  createLazyLoadData();
  const dataSource = [...initialState.data];
  const query = new Query();
  switch (action.type) {
    case Grid_FetchData: {
      // grouping
      if (!isNullOrUndefined(action.payload.group)) {
          applyGrouping(query, action.payload.group);
      }
      // lazy load goruping
      applyLazyLoad(query, action.payload);
      // To get the count of the data
      query.isCountRequired = true;
      // Execute local data operations using the provided query
      const currentResult = new DataManager(dataSource).executeLocal(query);
      // Return the result along with the count of total records
      return ({
        data: {
          result: currentResult.result, // Result of the data
          count: currentResult.count // Total record count
        } 
      })
    }
  }
}

Further information can be accessed in the respective documentation for lazy load grouping.

The complete example is available in the handling CRUD operations topic.

Handling CRUD operations

The Grid component provides powerful options for dynamically inserting, deleting, and updating records, enabling you to modify data directly within the grid. This feature is useful when you want to perform CRUD (Create, Read, Update, Delete) operations seamlessly.

Integrating CRUD Operations

To implement CRUD operations using Syncfusion Grid, follow these steps:

  1. Configure grid settings: Set up the necessary grid settings, such as allowing editing, adding, and deleting records. Define the toolbar options to facilitate your interactions.

  2. Handle data state changes: Utilize the dataStateChange event to respond to changes in the grid’s data state. This event is triggered whenever you interact with the grid, such as paging or sorting.

  3. Execute CRUD operations: Within the event handler for dataSourceChanged, implement logic to handle various CRUD actions based on the action or requestType property of the event arguments.

  4. Call endEdit method: After performing CRUD operations (adding, editing, or deleting), call the endEdit method to signal the completion of the operation and update the grid accordingly.

Insert operation

When an insert operation is performed in the grid, the dataSourceChanged event will be triggered, allowing access to the following referenced arguments within the event.

Adding

const reducer = (state = initialState, action) => {
  createLazyLoadData();
  const dataSource = [...initialState.data];
  const query = new Query();
  switch (action.type) {
    case Grid_Add: {
      // Perform add operation
      gridData.insert(action.payload.data, '', undefined, 0);
      const addedData = gridData.executeLocal(new Query());
      initialState.data = [...addedData];
      const count = addedData.length;
      const result = new DataManager(addedData).executeLocal(action.gridQuery);
      const currentPageData = new DataManager(result).executeLocal(new Query().skip(action.payload.state.skip).take(action.payload.state.take));
      return ({
          data: { result: currentPageData, count: filter.length ? result.length : count },
      })
    }
  }
}

Edit operation

When an edit operation is performed in the grid, the dataSourceChanged event will be triggered, providing access to the following referenced arguments within the event.

Edit record

const reducer = (state = initialState, action) => {
  createLazyLoadData();
  const dataSource = [...initialState.data];
  const query = new Query();
  switch (action.type) {
    case Grid_Editing: {
      // Perform edit operation
      gridData.update('OrderID', action.payload.data);
      const updatedData = gridData.executeLocal(new Query());
      initialState.data = [...updatedData];
      const count = updatedData.length;
      const result = new DataManager(updatedData).executeLocal(action.gridQuery);
      const currentPageData = new DataManager(result).executeLocal(new Query().skip(action.payload.state.skip).take(action.payload.state.take));
      return ({
          data: { result: currentPageData, count: filter.length ? result.length : count }
      })
    }
  }
}

Delete operation

When a delete operation is performed in the grid, the dataSourceChanged event will be triggered, allowing access to the following referenced arguments within the event.

Delete record

const reducer = (state = initialState, action) => {
  createLazyLoadData();
  const dataSource = [...initialState.data];
  const query = new Query();
  switch (action.type) {
    case Grid_Delete: {
      // Perform delete operation
      gridData.remove('OrderID', { OrderID: action.payload.data[0]['OrderID'] });
      const updatedData = gridData.executeLocal(new Query());
      initialState.data = [...updatedData];
      const count = updatedData.length;
      const result = new DataManager(updatedData).executeLocal(action.gridQuery);
      const currentPageData = new DataManager(result).executeLocal(new Query().skip(action.payload.state.skip).take(action.payload.state.take));
      return ({
          data: { result: currentPageData, count: filter.length ? result.length : count }
      })
    }
  }
}

The steps below to create a Redux service.

Step 1 : Install Dependencies

In your terminal, navigate to your project directory and run the following command to install the required packages:

npm install --save redux react-redux

The above command install the redux package, which is the core Redux library, and the react-redux package, which provides integration between Redux and React.

Step 2 : Set Up Redux Store

Once the dependencies are installed, you need to set up a Redux store in your application. Add the following code to the store.tsx file.

  1. Create a new file called store.tsx in your project’s directory.

  2. In the store.tsx file, import the necessary Redux functions and create your store:

     import { createStore } from 'redux';
     import reducer from "../reducer";
     const store = createStore(reducer);
     export default store;

    In the above code, you import the createStore function from the redux package and your reducer from the appropriate file. Then, you create your Redux store using the createStore function, passing in your reducer function.

  3. Now, you have set up your Redux store. You can customize it further by adding middle ware, enhancers, or other configurations as needed.

Step 3 : Connect Redux Provider

To make the Redux store available to your React components, you need to wrap your application with the Redux Provider component. It uses the Redux store as a prop and connects all the components in its hierarchy. Follow these steps to connect the Redux provider:

  1. Open your root component file (usually index.ts).

  2. Import the necessary dependencies:

     import React from 'react';
     import ReactDOM from 'react-dom/client';
     import { Provider } from 'react-redux';
     import store from './store';
  3. Wrap your application component with the Provider component and pass the Redux store as a prop:

     ReactDOM.render(
       <Provider store={store}>
         <App />
       </Provider>,
     document.getElementById('root'));

Step 4: Create a Actions

According to Redux documentation, the Redux actions are plain JavaScript objects that describe changes to be made to the application’s state. These actions are dispatched to the Redux store, and reducers handle them to update the state accordingly.

The action type defines the name of the action that will be performed from your application. Each action type is a string that describes the specific operation being performed.

Here’s an example of a Redux action definition for sort operation . you can add the code in the action.tsx file.

export const Grid_FetchData = "Grid_FetchData";

export const fetchData = (state: any, query: Query) => ({
    type: "Grid_FetchData",
    payload: state,
    gridQuery: query
});

Step 5: Create a Reducer

According to the Redux documentation, the Redux reducers are functions responsible for handling actions and updating the state of the application. Reducers specify how the state should change in response to different actions dispatched to the Redux store.

Here’s an example of a Redux reducer definition for sorting operations. You can add the reducer code in the reducer.tsx file.

const initialState = {
    data: lazyLoadData,
    error: false,
    result: [],
    count: 0,
    allowPaging: true
}

const applyPaging = (query, page) => {
  if (page.take && page.skip) {
    const pageSkip = page.skip / page.take + 1;
    const pageTake = page.take;
    query.page(pageSkip, pageTake);
  }
  else if (page.skip === 0 && page.take) {
    query.page(1, page.take);
  }
}

const reducer = (state = initialState, action) => {
    createLazyLoadData();
    const dataSource = [...initialState.data];
    const query = new Query();
    switch (action.type) {
        case Grid_FetchData: {
            applyPaging(query, action.payload);
            if (action.payload.requiresCounts) {
                query.isCountRequired = true;
            }
            const currentResult = new DataManager(dataSource).executeLocal(query);
            return ({
                data: { result: currentResult.result, count: currentResult.count }
            })
        }
        default: {
            const currentResult = new DataManager(dataSource).executeLocal(query);
            return ({
                data: { result: currentResult.result , count: currentResult.count}
            })
        }
    }
}
export default reducer;

The grid expects an object as result and count format.

Step 6: Create a Dispatch

According the Redux documentation, the dispatch function is a method provided by the Redux store. It is used to send actions to the store, triggering the state update process. Dispatching an action is the primary way to update the state in a Redux application.

When performing actions such as adding, updating, or deleting a row in the grid, it dispatches the corresponding actions to modify the data in the store and trigger updates to the grid component.

When the dataSourceChanged and dataStateChange event occur, the dispatch method is called according to their grid actions.

Add the following code to the App.tsx file.

  const dataStateChange = (args: DataStateChangeEventArgs) => {
    const query = (gridInstance as GridComponent).getDataModule().generateQuery();
    dispatch(fetchData(args, query));
  }

  const dataSourceChanged = (args: DataSourceChangedEventArgs) => {
    const query = (gridInstance as GridComponent).getDataModule().generateQuery();
    if (state.requestType === 'delete') {
      dispatch(deleteRow(state, query));
    }
  }

Step 7: Fetching Grid Data using useEffect

To fetch grid data and populate the dataSource property of the grid, you can use the useEffect hook in React. The useEffect hook allows you to perform side effects, such as fetching data, after the component has rendered. Here’s an example of how to fetch grid data using useEffect:

Add the following code to the App.tsx file.

  useEffect(() => {
    if (gridInstance) {
      gridInstance.dataSource = state.data
    }
  },[state.data])

The following example demonstrates how to bind custom data to handle grid actions and CRUD operation.

import React, { useEffect} from 'react';
import { useDispatch, useSelector } from "react-redux";
import { ColumnDirective, ColumnsDirective, Filter, GridComponent, LazyLoadGroup, Inject, Page, Sort, Edit, Toolbar, Group } from '@syncfusion/ej2-react-grids'
import { addRow, deleteRow, fetchData, updateRow } from './reducer/action';

const App = () => {    
  let gridInstance;
  const toolbar = ['Add', 'Edit', 'Delete',  'Update', 'Cancel', 'Search'];
  const editSettings = {allowAdding: true, allowDeleting: true, allowEditing: true};
  const groupSettings={enableLazyLoading: true, columns:['ProductName'], showGroupedColumn: true};
  const filterSettings = {columns: [{field: 'CustomerName', matchCase: false, operator: 'startswith', predicate: 'and', value: 'Maria'}]}
  const sortSettings = {columns: [{ field: 'ProductID', direction: 'Descending' }]}
  const state = useSelector((state) => state);
  const dispatch = useDispatch();

  const dataStateChange = (state) => {
    const query = gridInstance.getDataModule().generateQuery();
    dispatch(fetchData(state, query));
  }

  const dataSourceChange = (state) =>{
    const query = gridInstance.getDataModule().generateQuery(true);  
    if (state.requestType === "save") {
      if (state.action === "add") {
        dispatch(addRow(state, query));
      } else if (state.action === "edit") {
        dispatch(updateRow(state, query));
      }
    }
    if (state.requestType === 'delete') {
      dispatch(deleteRow(state, query));
    }
  }

  useEffect(() => {
    if (gridInstance) {
      gridInstance.dataSource = state.data;
    }
  },[state.data])
  
  return (
      <GridComponent ref={grid => gridInstance = grid} allowSorting={true} allowFiltering={true} 
      allowPaging={true} 
      allowGrouping={true}
      filterSettings={filterSettings}
      groupSettings={groupSettings}
      sortSettings={sortSettings}
      toolbar={toolbar}
      editSettings={editSettings}
      dataStateChange={dataStateChange}
      dataSourceChanged={dataSourceChange}
      >
        <ColumnsDirective>
        <ColumnDirective field='OrderID' headerText='Order ID' textAlign="Right" width='120' isPrimaryKey={true} />
        <ColumnDirective field='ProductName' headerText='Product Name' width='160' />
        <ColumnDirective field='ProductID' headerText='Product ID' textAlign="Right" width='120' />
        <ColumnDirective field='CustomerName' headerText='Customer Name' width='160' />
      </ColumnsDirective>
      <Inject services={[Page, Sort, Filter, Group, LazyLoadGroup, Edit, Toolbar]} />
      </GridComponent>
  )
}

export default (App);
import React, { useEffect} from 'react';
import { useDispatch, useSelector } from "react-redux";
import { ColumnDirective, ColumnsDirective, Filter, GridComponent, DataStateChangeEventArgs, LazyLoadGroup, Inject, Page, Sort, Edit, Toolbar, GroupSettingsModel, Group, SortSettingsModel, FilterSettingsModel, DataSourceChangedEventArgs, ToolbarItems, EditSettingsModel } from '@syncfusion/ej2-react-grids'
import { addRow, deleteRow, fetchData, updateRow } from './reducer/action';

export interface gridState{
  data:{
    result:Object[];
    count:number;
  }
}
const App = () => {    
  var gridInstance: GridComponent|null;
  const toolbar: ToolbarItems[] = ['Add', 'Edit', 'Delete',  'Update', 'Cancel', 'Search'];
  const editSettings: EditSettingsModel = {allowAdding: true, allowDeleting: true, allowEditing: true};
  const groupSettings: GroupSettingsModel={enableLazyLoading: true, columns:['ProductName'], showGroupedColumn: true};
  const filterSettings:FilterSettingsModel = {columns: [{field: 'CustomerName', matchCase: false, operator: 'startswith', predicate: 'and', value: 'Maria'}]}
  const sortSettings:SortSettingsModel = {columns: [{ field: 'ProductID', direction: 'Descending' }]}
  const state = useSelector((state: gridState) => state);
  const dispatch = useDispatch();

  const dataStateChange = (state: DataStateChangeEventArgs) => {
    const query = (gridInstance as GridComponent).getDataModule().generateQuery();
    dispatch(fetchData(state, query));
  }

  const dataSourceChange = (state: DataSourceChangedEventArgs) =>{
    const query = (gridInstance as GridComponent).getDataModule().generateQuery(true);  
    if (state.requestType === "save") {
      if (state.action === "add") {
        dispatch(addRow(state, query));
      } else if (state.action === "edit") {
        dispatch(updateRow(state, query));
      }
    }
    if (state.requestType === 'delete') {
      dispatch(deleteRow(state, query));
    }
  }

  useEffect(() => {
    if (gridInstance) {
      gridInstance.dataSource = state.data;
    }
  },[state.data])
  
  return (
      <GridComponent ref={grid => gridInstance = grid} allowSorting={true} allowFiltering={true} 
      allowPaging={true} 
      allowGrouping={true}
      filterSettings={filterSettings}
      groupSettings={groupSettings}
      sortSettings={sortSettings}
      toolbar={toolbar}
      editSettings={editSettings}
      dataStateChange={dataStateChange}
      dataSourceChanged={dataSourceChange}
      >
        <ColumnsDirective>
        <ColumnDirective field='OrderID' headerText='Order ID' textAlign="Right" width='120' isPrimaryKey={true} />
        <ColumnDirective field='ProductName' headerText='Product Name' width='160' />
        <ColumnDirective field='ProductID' headerText='Product ID' textAlign="Right" width='120' />
        <ColumnDirective field='CustomerName' headerText='Customer Name' width='160' />
      </ColumnsDirective>
      <Inject services={[Page, Sort, Filter, Group, LazyLoadGroup, Edit, Toolbar]} />
      </GridComponent>
  )
}

export default (App);
import { Grid_Add, Grid_Delete, Grid_Editing, Grid_FetchData } from './action';
import { DataManager, Query } from '@syncfusion/ej2-data';
import { createLazyLoadData, lazyLoadData } from '../datasource';
import { DataStateChangeEventArgs, LazyLoadGroup } from '@syncfusion/ej2-react-grids';
import { isNullOrUndefined } from '@syncfusion/ej2-base';

const initialGrouping = { columns: ['ProductName'], enableLazyLoading: true };
const initialFiltering = { columns: [{ field: 'CustomerName', matchCase: false, operator: 'startswith', predicate: 'and', value: 'Maria' }] }
const initialSorting = { columns: [{ field: 'ProductID', direction: 'Descending' }] }
const initialPaging = { allowPaging: true };
const initialState = {
    data: lazyLoadData,
    error: false,
    result: [],
    count: 0,
}

const applyFiltering = (query, filter) => {
    // Check if filter columns are specified
    if (filter.columns && filter.columns.length) {
        // Apply filtering for each specified column
        for (let i = 0; i < filter.columns.length; i++) {
            const field = filter.columns[i].field;
            const operator = filter.columns[i].operator;
            const value = filter.columns[i].value;
            query.where(field, operator, value);
        }
    }
    else {
        // Apply filtering based on direct filter conditions
        for (let i = 0; i < filter.length; i++) {
            const { fn, e } = filter[i];
            if (fn === 'onWhere') {
                query.where(e);
            }
        }
    }
}

const applySearching = (query, search) => {
    // Check if a search operation is requested
    if (search && search.length > 0) {
        // Extract the search key and fields from the search array
        const { fields, key } = search[0];
        // perform search operation using the field and key on the query
        query.search(key, fields);
    }
}

const applySorting = (query, sorted) => {
    // Check if sorting data is available
    if (sorted && sorted.length > 0) {
        // Iterate through each sorting info
        sorted.forEach(sort => {
            // get the sort field name either by name or field
            const sortField = sort.name || sort.field;
            // Perform sort operation using the query based on the field name and direction
            query.sortBy(sortField, sort.direction);
        });
    }
}

const applyGrouping = (query, group) => {
    // Check if grouping data is available
    if (group && group.length > 0) {
        for (let i = 0; i < group.length; i++) {
            // perform group operation using the column on the query
            query.group(group[i]);
        }
    }
}

const applyLazyLoad = (query, payload) => {
    // Configure lazy loading for the main data
    if (payload.isLazyLoad) {
        query.lazyLoad.push({ key: 'isLazyLoad', value: true });
        // If on-demand group loading is enabled, configure lazy loading for grouped data
        if (payload.onDemandGroupInfo) {
            query.lazyLoad.push({
                key: 'onDemandGroupInfo',
                value: payload.action.lazyLoadQuery,
            });
        }
    }
}

const applyPaging = (query, page) => {
    // Check if both 'take' and 'skip' values are available
    if (page.take && page.skip) {
        // Calculate pageSkip and pageTake values to get pageIndex and pageSize
        const pageSkip = page.skip / page.take + 1;
        const pageTake = page.take;
        query.page(pageSkip, pageTake);
    }
    // If if only 'take' is available and 'skip' is 0, apply paging for the first page.
    else if (page.skip === 0 && page.take) {
        query.page(1, page.take);
    }
}

const reducer = (state = initialState, action) => {
    createLazyLoadData();
    const dataSource = [...initialState.data];
    const gridData = new DataManager(dataSource);
    const query = new Query();
    let filter = [];
    if (action.gridQuery !== undefined) {
        filter = action.gridQuery.queries.filter((fn) => {
            return fn.fn === "onWhere"
        })
    }
    switch (action.type) {
        case Grid_FetchData: {
            // filtering
            if (action.payload.where) {
                applyFiltering(query, action.gridQuery.queries);
            }
            // searching
            if (!isNullOrUndefined(action.payload.search)) {
                applySearching(query, action.payload.search);
            }
            // sorting
            if (!isNullOrUndefined(action.payload.sorted)) {
                applySorting(query, action.payload.sorted);
            }
            // grouping
            if (!isNullOrUndefined(action.payload.group)) {
                applyGrouping(query, action.payload.group);
            }
            // lazy load goruping
            applyLazyLoad(query, action.payload);
            // paging
            applyPaging(query, action.payload);
            // To get the count of the data
            if (action.payload.requiresCounts) {
                query.isCountRequired = true;
            }
            // Execute local data operations using the provided query
            const currentResult = new DataManager(dataSource).executeLocal(query);
            // Return the result along with the count of total records
            return ({
                data: {
                    result: currentResult.result, // Result of the data
                    count: currentResult.count // Total record count
                }
            })
        }
        case Grid_Add: {
            // Perform add operation
            gridData.insert(action.payload.data, '', undefined, 0);
            const addedData = gridData.executeLocal(new Query());
            initialState.data = [...addedData];
            const count = addedData.length;
            const result = new DataManager(addedData).executeLocal(action.gridQuery);
            const currentPageData = new DataManager(result).executeLocal(new Query().skip(action.payload.state.skip).take(action.payload.state.take));
            return ({
                data: { result: currentPageData, count: filter.length ? result.length : count },
            })
        }
        case Grid_Editing: {
            // Perform edit operation
            gridData.update('OrderID', action.payload.data);
            const updatedData = gridData.executeLocal(new Query());
            initialState.data = [...updatedData];
            const count = updatedData.length;
            const result = new DataManager(updatedData).executeLocal(action.gridQuery);
            const currentPageData = new DataManager(result).executeLocal(new Query().skip(action.payload.state.skip).take(action.payload.state.take));
            return ({
                data: { result: currentPageData, count: filter.length ? result.length : count }
            })
        }
        case Grid_Delete: {
            // perform delete operation
            gridData.remove('OrderID', { OrderID: action.payload.data[0]['OrderID'] });
            const updatedData = gridData.executeLocal(new Query());
            initialState.data = [...updatedData];
            const count = updatedData.length;
            const result = new DataManager(updatedData).executeLocal(action.gridQuery);
            const currentPageData = new DataManager(result).executeLocal(new Query().skip(action.payload.state.skip).take(action.payload.state.take));
            return ({
                data: { result: currentPageData, count: filter.length ? result.length : count }
            })
        }

        default: {
            // initial filtering
            if (initialFiltering.columns.length) {
                applyFiltering(query, initialFiltering.columns);
            }
            // initial sorting
            if (initialSorting.columns.length) {
                applySorting(query, initialSorting.columns);
            }
            // initial grouping
            if (initialGrouping.columns.length) {
                applyGrouping(query, initialGrouping.columns);
            }
            if (initialGrouping.enableLazyLoading) {
                query.lazyLoad.push({ key: 'isLazyLoad', value: true })
            }
            // initial paging
            if (initialPaging.allowPaging) {
                const pageSkip = 1;
                const pageTake = 12;
                query.page(pageSkip, pageTake);
            }
            // To get the count of the data
            query.isCountRequired = true;
            // Execute local data operations using the provided query
            const currentResult = new DataManager(dataSource).executeLocal(query);
            // Return the result along with the count of total records
            return ({
                data: {
                    result: currentResult.result, // Result of the data
                    count: currentResult.count // Total record count
                }
            })
        }
    }
}
export default reducer;
import { Grid_Add, Grid_Delete, Grid_Editing, Grid_FetchData } from './action';
import { DataManager, Query } from '@syncfusion/ej2-data';
import { createLazyLoadData, lazyLoadData } from '../datasource';
import { DataStateChangeEventArgs, LazyLoadGroup } from '@syncfusion/ej2-react-grids';
import { isNullOrUndefined } from '@syncfusion/ej2-base';

interface sortInfo {
    name?: string;
    field?: string;
    direction: string
}

interface payloadInfo {
    isLazyLoad: boolean;
    onDemandGroupInfo?: { [key: string]: number };
    action: {
        lazyLoadQuery: LazyLoadGroup;
    };
}

const initialGrouping = { columns: ['ProductName'], enableLazyLoading: true };
const initialFiltering = { columns: [{ field: 'CustomerName', matchCase: false, operator: 'startswith', predicate: 'and', value: 'Maria' }] }
const initialSorting = { columns: [{ field: 'ProductID', direction: 'Descending' }] }
const initialPaging = { allowPaging: true };
const initialState: any = {
    data: lazyLoadData,
    error: false,
    result: [],
    count: 0,
}

const applyFiltering = (query: Query, filter: any) => {
    // Check if filter columns are specified
    if (filter.columns && filter.columns.length) {
        // Apply filtering for each specified column
        for (let i = 0; i < filter.columns.length; i++) {
            const field = filter.columns[i].field;
            const operator = filter.columns[i].operator;
            const value = filter.columns[i].value;
            query.where(field, operator, value);
        }
    }
    else {
        // Apply filtering based on direct filter conditions
        for (let i = 0; i < filter.length; i++) {
            const { fn, e } = filter[i];
            if (fn === 'onWhere') {
                query.where(e as string);
            }
        }
    }
}

const applySearching = (query: Query, search: any) => {
    // Check if a search operation is requested
    if (search && search.length > 0) {
        // Extract the search key and fields from the search array
        const { fields, key } = search[0];
        // perform search operation using the field and key on the query
        query.search(key, fields);
    }
}

const applySorting = (query: Query, sorted: sortInfo[]) => {
    // Check if sorting data is available
    if (sorted && sorted.length > 0) {
        // Iterate through each sorting info
        sorted.forEach(sort => {
            // get the sort field name either by name or field
            const sortField = sort.name || sort.field;
            // Perform sort operation using the query based on the field name and direction
            query.sortBy(sortField as string, sort.direction);
        });
    }
}

const applyGrouping = (query: Query, group: Object[]) => {
    // Check if grouping data is available
    if (group && group.length > 0) {
        for (let i = 0; i < group.length; i++) {
            // perform group operation using the column on the query
            query.group(group[i] as string);
        }
    }
}

const applyLazyLoad = (query: Query, payload: payloadInfo) => {
    // Configure lazy loading for the main data
    if (payload.isLazyLoad) {
        query.lazyLoad.push({ key: 'isLazyLoad', value: true });
        // If on-demand group loading is enabled, configure lazy loading for grouped data
        if (payload.onDemandGroupInfo) {
            query.lazyLoad.push({
                key: 'onDemandGroupInfo',
                value: payload.action.lazyLoadQuery,
            });
        }
    }
}

const applyPaging = (query: Query, page: DataStateChangeEventArgs) => {
    // Check if both 'take' and 'skip' values are available
    if (page.take && page.skip) {
        // Calculate pageSkip and pageTake values to get pageIndex and pageSize
        const pageSkip = page.skip / page.take + 1;
        const pageTake = page.take;
        query.page(pageSkip, pageTake);
    }
    // If if only 'take' is available and 'skip' is 0, apply paging for the first page.
    else if (page.skip === 0 && page.take) {
        query.page(1, page.take);
    }
}

const reducer = (state = initialState, action: any) => {
    createLazyLoadData();
    const dataSource = [...initialState.data];
    const gridData = new DataManager(dataSource);
    const query = new Query();
    let filter: any = [];
    if (action.gridQuery !== undefined) {
        filter = action.gridQuery.queries.filter((fn: any) => {
            return fn.fn === "onWhere"
        })
    }
    switch (action.type) {
        case Grid_FetchData: {
            // filtering
            if (action.payload.where) {
                applyFiltering(query, action.gridQuery.queries);
            }
            // searching
            if (!isNullOrUndefined(action.payload.search)) {
                applySearching(query, action.payload.search);
            }
            // sorting
            if (!isNullOrUndefined(action.payload.sorted)) {
                applySorting(query, action.payload.sorted);
            }
            // grouping
            if (!isNullOrUndefined(action.payload.group)) {
                applyGrouping(query, action.payload.group);
            }
            // lazy load goruping
            applyLazyLoad(query, action.payload);
            // paging
            applyPaging(query, action.payload);
            // To get the count of the data
            if (action.payload.requiresCounts) {
                query.isCountRequired = true;
            }
            // Execute local data operations using the provided query
            const currentResult: any = new DataManager(dataSource).executeLocal(query);
            // Return the result along with the count of total records
            return ({
                data: {
                    result: currentResult.result, // Result of the data
                    count: currentResult.count
                } // Total record count
            })
        }
        case Grid_Add: {
            // Perform add operation
            gridData.insert(action.payload.data, '', undefined, 0);
            const addedData = gridData.executeLocal(new Query());
            initialState.data = [...addedData];
            const count = addedData.length;
            const result = new DataManager(addedData).executeLocal(action.gridQuery);
            const currentPageData = new DataManager(result).executeLocal(new Query().skip(action.payload.state.skip).take(action.payload.state.take));
            return ({
                data: { result: currentPageData, count: filter.length ? result.length : count },
            })
        }
        case Grid_Editing: {
            // Perform edit operation
            gridData.update('OrderID', action.payload.data);
            const updatedData = gridData.executeLocal(new Query());
            initialState.data = [...updatedData];
            const count = updatedData.length;
            const result = new DataManager(updatedData).executeLocal(action.gridQuery);
            const currentPageData = new DataManager(result).executeLocal(new Query().skip(action.payload.state.skip).take(action.payload.state.take));
            return ({
                data: { result: currentPageData, count: filter.length ? result.length : count }
            })
        }
        case Grid_Delete: {
            // Perform delete operation
            gridData.remove('OrderID', { OrderID: action.payload.data[0]['OrderID'] });
            const updatedData = gridData.executeLocal(new Query());
            initialState.data = [...updatedData];
            const count = updatedData.length;
            const result = new DataManager(updatedData).executeLocal(action.gridQuery);
            const currentPageData = new DataManager(result).executeLocal(new Query().skip(action.payload.state.skip).take(action.payload.state.take));
            return ({
                data: { result: currentPageData, count: filter.length ? result.length : count }
            })
        }

        default: {
            // initial filtering
            if (initialFiltering.columns.length) {
                applyFiltering(query, initialFiltering.columns);
            }
            // initial sorting
            if (initialSorting.columns.length) {
                applySorting(query, initialSorting.columns);
            }
            // initial grouping
            if (initialGrouping.columns.length) {
                applyGrouping(query, initialGrouping.columns);
            }
            if (initialGrouping.enableLazyLoading) {
                query.lazyLoad.push({ key: 'isLazyLoad', value: true })
            }
            // initial paging
            if (initialPaging.allowPaging) {
                const pageSkip = 1;
                const pageTake = 12;
                query.page(pageSkip, pageTake);
            }
            // To get the count of the data
            query.isCountRequired = true;
            // Execute local data operations using the provided query
            const currentResult = new DataManager(dataSource).executeLocal(query);
            // Return the result along with the count of total records
            return ({
                data: {
                    result: currentResult.result, // Result of the data
                    count: currentResult.count // Total record count
                }
            })
        }
    }
}
export default reducer;
import { Query } from "@syncfusion/ej2-data";

export const Grid_FetchData = "Grid_FetchData";
export const Grid_Add = "Grid_Add";
export const Grid_Delete = "Grid_Delete";
export const Grid_Editing = "Grid_Editing";

export const fetchData = (state, query) => ({
    type: "Grid_FetchData",
    payload: state,
    gridQuery: query
});

export const addRow = (state, query) => ({
    type: "Grid_Add",
    payload: state,
    gridQuery: query
});

export const deleteRow = (state, query) => ({
    type: "Grid_Delete",
    payload: state,
    gridQuery: query
});

export const updateRow = (state, query) => ({
    type: "Grid_Editing",
    payload: state,
    gridQuery: query
});
import { Query } from "@syncfusion/ej2-data";

export const Grid_FetchData = "Grid_FetchData";
export const Grid_Add = "Grid_Add";
export const Grid_Delete = "Grid_Delete";
export const Grid_Editing = "Grid_Editing";

export const fetchData = (state: any, query: Query) => ({
    type: "Grid_FetchData",
    payload: state,
    gridQuery: query
});

export const addRow = (state: any, query: Query) => ({
    type: "Grid_Add",
    payload: state,
    gridQuery: query
});

export const deleteRow = (state: any, query: Query) => ({
    type: "Grid_Delete",
    payload: state,
    gridQuery: query
});

export const updateRow = (state: any, query: Query) => ({
    type: "Grid_Editing",
    payload: state,
    gridQuery: query
});
export let lazyLoadData = [];
export function createLazyLoadData() {
    if (lazyLoadData.length) {
        return;
    }
    let customerid = ['VINET', 'TOMSP', 'HANAR', 'VICTE', 'SUPRD', 'HANAR', 'CHOPS', 'RICSU', 'WELLI', 'HILAA',
        'ERNSH', 'CENTC', 'OTTIK', 'QUEDE', 'RATTC', 'ERNSH', 'FOLKO', 'BLONP', 'WARTH', 'FRANK', 'GROSR', 'WHITC', 'WARTH',
        'SPLIR', 'RATTC', 'QUICK', 'VINET', 'MAGAA', 'TORTU', 'MORGK', 'BERGS', 'LEHMS', 'BERGS', 'ROMEY', 'ROMEY', 'LILAS',
        'LEHMS', 'QUICK', 'QUICK', 'RICAR', 'REGGC', 'BSBEV', 'COMMI', 'QUEDE', 'TRADH', 'TORTU', 'RATTC', 'VINET', 'LILAS',
        'BLONP', 'HUNGO', 'RICAR', 'MAGAA', 'WANDK', 'SUPRD', 'GODOS', 'TORTU', 'OLDWO', 'ROMEY', 'LONEP', 'ANATR', 'HUNGO',
        'THEBI', 'DUMON', 'WANDK', 'QUICK', 'RATTC', 'ISLAT', 'RATTC', 'LONEP', 'ISLAT', 'TORTU', 'WARTH', 'ISLAT', 'PERIC',
        'KOENE', 'SAVEA', 'KOENE', 'BOLID', 'FOLKO', 'FURIB', 'SPLIR', 'LILAS', 'BONAP', 'MEREP', 'WARTH', 'VICTE',
        'HUNGO', 'PRINI', 'FRANK', 'OLDWO', 'MEREP', 'BONAP', 'SIMOB', 'FRANK', 'LEHMS', 'WHITC', 'QUICK', 'RATTC', 'FAMIA'];

    let product = ['Chai', 'Chang', 'Aniseed Syrup', 'Chef Anton\'s Cajun Seasoning', 'Chef Anton\'s Gumbo Mix',
        'Grandma\'s Boysenberry Spread', 'Uncle Bob\'s Organic Dried Pears', 'Northwoods Cranberry Sauce', 'Mishi Kobe Niku',
        'Ikura', 'Queso Cabrales', 'Queso Manchego La Pastora', 'Konbu', 'Tofu', 'Genen Shouyu', 'Pavlova', 'Alice Mutton',
        'Carnarvon Tigers', 'Teatime Chocolate Biscuits', 'Sir Rodney\'s Marmalade', 'Sir Rodney\'s Scones',
        'Gustaf\'s Knäckebröd', 'Tunnbröd', 'Guaraná Fantástica', 'NuNuCa Nuß-Nougat-Creme', 'Gumbär Gummibärchen',
        'Schoggi Schokolade', 'Rössle Sauerkraut', 'Thüringer Rostbratwurst', 'Nord-Ost Matjeshering', 'Gorgonzola Telino',
        'Mascarpone Fabioli', 'Geitost', 'Sasquatch Ale', 'Steeleye Stout', 'Inlagd Sill',
        'Gravad lax', 'Côte de Blaye', 'Chartreuse verte', 'Boston Crab Meat', 'Jack\'s New England Clam Chowder',
        'Singaporean Hokkien Fried Mee', 'Ipoh Coffee', 'Gula Malacca', 'Rogede sild', 'Spegesild', 'Zaanse koeken',
        'Chocolade', 'Maxilaku', 'Valkoinen suklaa', 'Manjimup Dried Apples', 'Filo Mix', 'Perth Pasties',
        'Tourtière', 'Pâté chinois', 'Gnocchi di nonna Alice', 'Ravioli Angelo', 'Escargots de Bourgogne',
        'Raclette Courdavault', 'Camembert Pierrot', 'Sirop d\'érable',
        'Tarte au sucre', 'Vegie-spread', 'Wimmers gute Semmelknödel', 'Louisiana Fiery Hot Pepper Sauce',
        'Louisiana Hot Spiced Okra', 'Laughing Lumberjack Lager', 'Scottish Longbreads',
        'Gudbrandsdalsost', 'Outback Lager', 'Flotemysost', 'Mozzarella di Giovanni', 'Röd Kaviar', 'Longlife Tofu',
        'Rhönbräu Klosterbier', 'Lakkalikööri', 'Original Frankfurter grüne Soße'];

    let customername = ['Maria', 'Ana Trujillo', 'Antonio Moreno', 'Thomas Hardy', 'Christina Berglund',
        'Hanna Moos', 'Frédérique Citeaux', 'Martín Sommer', 'Laurence Lebihan', 'Elizabeth Lincoln',
        'Victoria Ashworth', 'Patricio Simpson', 'Francisco Chang', 'Yang Wang', 'Pedro Afonso', 'Elizabeth Brown',
        'Sven Ottlieb', 'Janine Labrune', 'Ann Devon', 'Roland Mendel', 'Aria Cruz', 'Diego Roel',
        'Martine Rancé', 'Maria Larsson', 'Peter Franken', 'Carine Schmitt', 'Paolo Accorti', 'Lino Rodriguez',
        'Eduardo Saavedra', 'José Pedro Freyre', 'André Fonseca', 'Howard Snyder', 'Manuel Pereira',
        'Mario Pontes', 'Carlos Hernández', 'Yoshi Latimer', 'Patricia McKenna', 'Helen Bennett', 'Philip Cramer',
        'Daniel Tonini', 'Annette Roulet', 'Yoshi Tannamuri', 'John Steel', 'Renate Messner', 'Jaime Yorres',
        'Carlos González', 'Felipe Izquierdo', 'Fran Wilson', 'Giovanni Rovelli', 'Catherine Dewey', 'Jean Fresnière',
        'Alexander Feuer', 'Simon Crowther', 'Yvonne Moncada', 'Rene Phillips', 'Henriette Pfalzheim',
        'Marie Bertrand', 'Guillermo Fernández', 'Georg Pipps', 'Isabel de Castro', 'Bernardo Batista', 'Lúcia Carvalho',
        'Horst Kloss', 'Sergio Gutiérrez', 'Paula Wilson', 'Maurizio Moroni', 'Janete Limeira', 'Michael Holz',
        'Alejandra Camino', 'Jonas Bergulfsen', 'Jose Pavarotti', 'Hari Kumar', 'Jytte Petersen', 'Dominique Perrier',
        'Art Braunschweiger', 'Pascale Cartrain', 'Liz Nixon', 'Liu Wong', 'Karin Josephs', 'Miguel Angel Paolino',
        'Anabela Domingues', 'Helvetius Nagy', 'Palle Ibsen', 'Mary Saveley', 'Paul Henriot', 'Rita Müller',
        'Pirkko Koskitalo', 'Paula Parente', 'Karl Jablonski', 'Matti Karttunen', 'Zbyszek Piestrzeniewicz'];

    let customeraddress = ['507 - 20th Ave. E.\r\nApt. 2A', '908 W. Capital Way', '722 Moss Bay Blvd.',
        '4110 Old Redmond Rd.', '14 Garrett Hill', 'Coventry House\r\nMiner Rd.', 'Edgeham Hollow\r\nWinchester Way',
        '4726 - 11th Ave. N.E.', '7 Houndstooth Rd.', '59 rue de l\'Abbaye', 'Luisenstr. 48', '908 W. Capital Way',
        '722 Moss Bay Blvd.', '4110 Old Redmond Rd.', '14 Garrett Hill', 'Coventry House\r\nMiner Rd.',
        'Edgeham Hollow\r\nWinchester Way',
        '7 Houndstooth Rd.', '2817 Milton Dr.', 'Kirchgasse 6', 'Sierras de Granada 9993', 'Mehrheimerstr. 369',
        'Rua da Panificadora, 12', '2817 Milton Dr.', 'Mehrheimerstr. 369'];

    let quantityperunit = ['10 boxes x 20 bags', '24 - 12 oz bottles', '12 - 550 ml bottles',
        '48 - 6 oz jars', '36 boxes', '12 - 8 oz jars', '12 - 1 lb pkgs.', '12 - 12 oz jars',
        '18 - 500 g pkgs.', '12 - 200 ml jars',
        '1 kg pkg.', '10 - 500 g pkgs.', '2 kg box', '40 - 100 g pkgs.', '24 - 250 ml bottles', '32 - 500 g boxes',
        '20 - 1 kg tins', '16 kg pkg.', '10 boxes x 12 pieces', '30 gift boxes', '24 pkgs. x 4 pieces', '24 - 500 g pkgs.',
        '12 - 250 g pkgs.',
        '12 - 355 ml cans', '20 - 450 g glasses', '100 - 250 g bags'];

    let orderID = 10248;
    for (let i = 0; i < 20000; i++) {
        lazyLoadData.push({
            'OrderID': orderID + i,
            'CustomerID': customerid[Math.floor(Math.random() * customerid.length)],
            'CustomerName': customername[Math.floor(Math.random() * customername.length)],
            'CustomerAddress': customeraddress[Math.floor(Math.random() * customeraddress.length)],
            'ProductName': product[Math.floor(Math.random() * product.length)],
            'ProductID': i,
            'Quantity': quantityperunit[Math.floor(Math.random() * quantityperunit.length)]
        });
    }
}
export let lazyLoadData: Object[] = [];
export function createLazyLoadData(): void {
    if (lazyLoadData.length) {
        return;
    }
    let customerid: string[] = ['VINET', 'TOMSP', 'HANAR', 'VICTE', 'SUPRD', 'HANAR', 'CHOPS', 'RICSU', 'WELLI', 'HILAA',
        'ERNSH', 'CENTC', 'OTTIK', 'QUEDE', 'RATTC', 'ERNSH', 'FOLKO', 'BLONP', 'WARTH', 'FRANK', 'GROSR', 'WHITC', 'WARTH',
        'SPLIR', 'RATTC', 'QUICK', 'VINET', 'MAGAA', 'TORTU', 'MORGK', 'BERGS', 'LEHMS', 'BERGS', 'ROMEY', 'ROMEY', 'LILAS',
        'LEHMS', 'QUICK', 'QUICK', 'RICAR', 'REGGC', 'BSBEV', 'COMMI', 'QUEDE', 'TRADH', 'TORTU', 'RATTC', 'VINET', 'LILAS',
        'BLONP', 'HUNGO', 'RICAR', 'MAGAA', 'WANDK', 'SUPRD', 'GODOS', 'TORTU', 'OLDWO', 'ROMEY', 'LONEP', 'ANATR', 'HUNGO',
        'THEBI', 'DUMON', 'WANDK', 'QUICK', 'RATTC', 'ISLAT', 'RATTC', 'LONEP', 'ISLAT', 'TORTU', 'WARTH', 'ISLAT', 'PERIC',
        'KOENE', 'SAVEA', 'KOENE', 'BOLID', 'FOLKO', 'FURIB', 'SPLIR', 'LILAS', 'BONAP', 'MEREP', 'WARTH', 'VICTE',
        'HUNGO', 'PRINI', 'FRANK', 'OLDWO', 'MEREP', 'BONAP', 'SIMOB', 'FRANK', 'LEHMS', 'WHITC', 'QUICK', 'RATTC', 'FAMIA'];

    let product: string[] = ['Chai', 'Chang', 'Aniseed Syrup', 'Chef Anton\'s Cajun Seasoning', 'Chef Anton\'s Gumbo Mix',
        'Grandma\'s Boysenberry Spread', 'Uncle Bob\'s Organic Dried Pears', 'Northwoods Cranberry Sauce', 'Mishi Kobe Niku',
        'Ikura', 'Queso Cabrales', 'Queso Manchego La Pastora', 'Konbu', 'Tofu', 'Genen Shouyu', 'Pavlova', 'Alice Mutton',
        'Carnarvon Tigers', 'Teatime Chocolate Biscuits', 'Sir Rodney\'s Marmalade', 'Sir Rodney\'s Scones',
        'Gustaf\'s Knäckebröd', 'Tunnbröd', 'Guaraná Fantástica', 'NuNuCa Nuß-Nougat-Creme', 'Gumbär Gummibärchen',
        'Schoggi Schokolade', 'Rössle Sauerkraut', 'Thüringer Rostbratwurst', 'Nord-Ost Matjeshering', 'Gorgonzola Telino',
        'Mascarpone Fabioli', 'Geitost', 'Sasquatch Ale', 'Steeleye Stout', 'Inlagd Sill',
        'Gravad lax', 'Côte de Blaye', 'Chartreuse verte', 'Boston Crab Meat', 'Jack\'s New England Clam Chowder',
        'Singaporean Hokkien Fried Mee', 'Ipoh Coffee', 'Gula Malacca', 'Rogede sild', 'Spegesild', 'Zaanse koeken',
        'Chocolade', 'Maxilaku', 'Valkoinen suklaa', 'Manjimup Dried Apples', 'Filo Mix', 'Perth Pasties',
        'Tourtière', 'Pâté chinois', 'Gnocchi di nonna Alice', 'Ravioli Angelo', 'Escargots de Bourgogne',
        'Raclette Courdavault', 'Camembert Pierrot', 'Sirop d\'érable',
        'Tarte au sucre', 'Vegie-spread', 'Wimmers gute Semmelknödel', 'Louisiana Fiery Hot Pepper Sauce',
        'Louisiana Hot Spiced Okra', 'Laughing Lumberjack Lager', 'Scottish Longbreads',
        'Gudbrandsdalsost', 'Outback Lager', 'Flotemysost', 'Mozzarella di Giovanni', 'Röd Kaviar', 'Longlife Tofu',
        'Rhönbräu Klosterbier', 'Lakkalikööri', 'Original Frankfurter grüne Soße'];

    let customername: string[] = ['Maria', 'Ana Trujillo', 'Antonio Moreno', 'Thomas Hardy', 'Christina Berglund',
        'Hanna Moos', 'Frédérique Citeaux', 'Martín Sommer', 'Laurence Lebihan', 'Elizabeth Lincoln',
        'Victoria Ashworth', 'Patricio Simpson', 'Francisco Chang', 'Yang Wang', 'Pedro Afonso', 'Elizabeth Brown',
        'Sven Ottlieb', 'Janine Labrune', 'Ann Devon', 'Roland Mendel', 'Aria Cruz', 'Diego Roel',
        'Martine Rancé', 'Maria Larsson', 'Peter Franken', 'Carine Schmitt', 'Paolo Accorti', 'Lino Rodriguez',
        'Eduardo Saavedra', 'José Pedro Freyre', 'André Fonseca', 'Howard Snyder', 'Manuel Pereira',
        'Mario Pontes', 'Carlos Hernández', 'Yoshi Latimer', 'Patricia McKenna', 'Helen Bennett', 'Philip Cramer',
        'Daniel Tonini', 'Annette Roulet', 'Yoshi Tannamuri', 'John Steel', 'Renate Messner', 'Jaime Yorres',
        'Carlos González', 'Felipe Izquierdo', 'Fran Wilson', 'Giovanni Rovelli', 'Catherine Dewey', 'Jean Fresnière',
        'Alexander Feuer', 'Simon Crowther', 'Yvonne Moncada', 'Rene Phillips', 'Henriette Pfalzheim',
        'Marie Bertrand', 'Guillermo Fernández', 'Georg Pipps', 'Isabel de Castro', 'Bernardo Batista', 'Lúcia Carvalho',
        'Horst Kloss', 'Sergio Gutiérrez', 'Paula Wilson', 'Maurizio Moroni', 'Janete Limeira', 'Michael Holz',
        'Alejandra Camino', 'Jonas Bergulfsen', 'Jose Pavarotti', 'Hari Kumar', 'Jytte Petersen', 'Dominique Perrier',
        'Art Braunschweiger', 'Pascale Cartrain', 'Liz Nixon', 'Liu Wong', 'Karin Josephs', 'Miguel Angel Paolino',
        'Anabela Domingues', 'Helvetius Nagy', 'Palle Ibsen', 'Mary Saveley', 'Paul Henriot', 'Rita Müller',
        'Pirkko Koskitalo', 'Paula Parente', 'Karl Jablonski', 'Matti Karttunen', 'Zbyszek Piestrzeniewicz'];

    let customeraddress: string[] = ['507 - 20th Ave. E.\r\nApt. 2A', '908 W. Capital Way', '722 Moss Bay Blvd.',
        '4110 Old Redmond Rd.', '14 Garrett Hill', 'Coventry House\r\nMiner Rd.', 'Edgeham Hollow\r\nWinchester Way',
        '4726 - 11th Ave. N.E.', '7 Houndstooth Rd.', '59 rue de l\'Abbaye', 'Luisenstr. 48', '908 W. Capital Way',
        '722 Moss Bay Blvd.', '4110 Old Redmond Rd.', '14 Garrett Hill', 'Coventry House\r\nMiner Rd.',
        'Edgeham Hollow\r\nWinchester Way',
        '7 Houndstooth Rd.', '2817 Milton Dr.', 'Kirchgasse 6', 'Sierras de Granada 9993', 'Mehrheimerstr. 369',
        'Rua da Panificadora, 12', '2817 Milton Dr.', 'Mehrheimerstr. 369'];

    let quantityperunit: string[] = ['10 boxes x 20 bags', '24 - 12 oz bottles', '12 - 550 ml bottles',
        '48 - 6 oz jars', '36 boxes', '12 - 8 oz jars', '12 - 1 lb pkgs.', '12 - 12 oz jars',
        '18 - 500 g pkgs.', '12 - 200 ml jars',
        '1 kg pkg.', '10 - 500 g pkgs.', '2 kg box', '40 - 100 g pkgs.', '24 - 250 ml bottles', '32 - 500 g boxes',
        '20 - 1 kg tins', '16 kg pkg.', '10 boxes x 12 pieces', '30 gift boxes', '24 pkgs. x 4 pieces', '24 - 500 g pkgs.',
        '12 - 250 g pkgs.',
        '12 - 355 ml cans', '20 - 450 g glasses', '100 - 250 g bags'];

    let orderID: number = 10248;
    for (let i: number = 0; i < 20000; i++) {
        lazyLoadData.push({
            'OrderID': orderID + i,
            'CustomerID': customerid[Math.floor(Math.random() * customerid.length)],
            'CustomerName': customername[Math.floor(Math.random() * customername.length)],
            'CustomerAddress': customeraddress[Math.floor(Math.random() * customeraddress.length)],
            'ProductName': product[Math.floor(Math.random() * product.length)],
            'ProductID': i,
            'Quantity': quantityperunit[Math.floor(Math.random() * quantityperunit.length)]
        });
    }
}
  • While working with grid edit operation, defining the isPrimaryKey property of column is a mandatory step. In case the primary key column is not defined, the edit or delete action will take place on the first row of the grid.
  • Need to maintain the same instance for all grid actions.

Sending additional parameters to the server

The Syncfusion Grid component allows you to include custom parameters in data requests. This feature is particularly useful when you need to provide additional information to the server enhanced processing.

By utilizing the query property of the grid along with the addParams method of the Query class, you can easily incorporate custom parameters into data requests for every grid action.

To enable custom parameters in data requests for the grid component, follow these steps:

1. Bind the Query Object to the Grid: Assign the initialized query object to the query property of the Syncfusion Grid component.

2. Initialize the Query Object: Create a new instance of the Query class and use the addParams method to add the custom parameters.

3. Handle Data State Changes: If you need to dynamically update the data based on interactions, implement the dataStateChange event handler to execute the query with the updated state.

4. Execute Data Request: In the service, execute the data request by combining the custom parameters with other query parameters such as paging and sorting.

The following example demonstrates how to send additional parameters to the server.

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from "react-redux";
import { ColumnDirective, ColumnsDirective, GridComponent, DataStateChangeEventArgs, Inject, Page, Sort, SortSettingsModel } from '@syncfusion/ej2-react-grids'
import { fetchData } from './reducer/action';
import { Query } from '@syncfusion/ej2-data';

const App = () => {
    let gridInstance;
    const sortSettings = { columns: [{ field: 'ProductID', direction: 'Descending' }] }
    const query = new Query().addParams('Syncfusion_React_Grid', 'true');
    const state = useSelector((state) => state);
    const dispatch = useDispatch();

    const dataStateChange = (state) => {
        dispatch(fetchData(state));
    }

    useEffect(() => {
        if (gridInstance) {
            gridInstance.dataSource = state.data;
        }
    }, [state.data])

    return (
        <GridComponent ref={grid => gridInstance = grid} query={query} allowSorting={true}
            allowPaging={true}
            sortSettings={sortSettings}
            dataStateChange={dataStateChange}
        >
            <ColumnsDirective>
                <ColumnDirective field='OrderID' headerText='Order ID' textAlign="Right" width='120' isPrimaryKey={true} />
                <ColumnDirective field='ProductName' headerText='Product Name' width='160' />
                <ColumnDirective field='ProductID' headerText='Product ID' textAlign="Right" width='120' />
                <ColumnDirective field='CustomerName' headerText='Customer Name' width='160' />
            </ColumnsDirective>
            <Inject services={[Page, Sort]} />
        </GridComponent>
    )
}

export default (App);
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from "react-redux";
import { ColumnDirective, ColumnsDirective, GridComponent, DataStateChangeEventArgs, Inject, Page, Sort, SortSettingsModel } from '@syncfusion/ej2-react-grids'
import { fetchData } from './reducer/action';
import { Query } from '@syncfusion/ej2-data';

export interface gridState {
  data: {
    result: Object[];
    count: number;
  }
}
const App = () => {
  let gridInstance: GridComponent | null;
  const sortSettings: SortSettingsModel = { columns: [{ field: 'ProductID', direction: 'Descending' }] }
  const query: Query = new Query().addParams('Syncfusion_React_Grid', 'true');
  const state = useSelector((state: gridState) => state);
  const dispatch = useDispatch();

  const dataStateChange = (state: DataStateChangeEventArgs) => {
    dispatch(fetchData(state));
  }

  useEffect(() => {
    if (gridInstance) {
      gridInstance.dataSource = state.data;
    }
  }, [state.data])

  return (
    <GridComponent ref={grid => gridInstance = grid} query={query} allowSorting={true}
      allowPaging={true}
      sortSettings={sortSettings}
      dataStateChange={dataStateChange}
    >
      <ColumnsDirective>
        <ColumnDirective field='OrderID' headerText='Order ID' textAlign="Right" width='120' isPrimaryKey={true} />
        <ColumnDirective field='ProductName' headerText='Product Name' width='160' />
        <ColumnDirective field='ProductID' headerText='Product ID' textAlign="Right" width='120' />
        <ColumnDirective field='CustomerName' headerText='Customer Name' width='160' />
      </ColumnsDirective>
      <Inject services={[Page, Sort]} />
    </GridComponent>
  )
}

export default (App);
import { Grid_FetchData } from './action';
import { DataManager, Query } from '@syncfusion/ej2-data';
import { createLazyLoadData, lazyLoadData } from '../datasource';
import { isNullOrUndefined } from '@syncfusion/ej2-base';

const initialSorting = { columns: [{ field: 'ProductID', direction: 'Descending' }] }
const initialParams = new Query().addParams('Syncfusion_React_Grid', 'true');
const initialPaging = { allowPaging: true };
const initialState = {
    data: lazyLoadData,
    error: false,
    result: [],
    count: 0,
}

const applySorting = (query, sorted) => {
    if (sorted && sorted.length > 0) {
        sorted.forEach(sort => {
            const sortField = sort.name || sort.field;
            query.sortBy(sortField, sort.direction);
        });
    }
}

const applyPage = (query, page) => {
    if (page.take && page.skip) {
        const pageSkip = page.skip / page.take + 1;
        const pageTake = page.take;
        query.page(pageSkip, pageTake);
    }
    else if (page.skip === 0 && page.take) {
        query.page(1, page.take);
    }
}

const reducer = (state = initialState, action) => {
    createLazyLoadData();
    const dataSource = [...initialState.data];
    let query = new Query();
    switch (action.type) {
        case Grid_FetchData: {
            //sorting
            if (!isNullOrUndefined(action.payload.sorted)) {
                applySorting(query, action.payload.sorted);
            }

            //paging
            applyPage(query, action.payload);

            //addParams
            if (action.payload.params.length) {
                query.addParams('Syncfusion_React_Grid', 'true');
            }

            if (action.payload.requiresCounts) {
                query.isCountRequired = true;
            }
            const currentResult = new DataManager(dataSource).executeLocal(query);
            return ({
                data: {
                    result: currentResult.result,
                    count: currentResult.count
                }
            })
        }

        default:
            if (initialSorting.columns.length) {
                applySorting(query, initialSorting.columns);
            }
            if (initialPaging.allowPaging) {
                const pageSkip = 1;
                const pageTake = 12;
                query.page(pageSkip, pageTake);
            }
            if (initialParams.params.length) {
                query.addParams('Syncfusion_React_Grid', 'true');
            }
            query.isCountRequired = true;
            const currentResult = new DataManager(dataSource).executeLocal(query);
            return ({
                data: { result: currentResult.result, count: currentResult.count }
            })
    }
}
export default reducer;
import { Grid_FetchData } from './action';
import { DataManager, Query } from '@syncfusion/ej2-data';
import { createLazyLoadData, lazyLoadData } from '../datasource';
import { DataStateChangeEventArgs } from '@syncfusion/ej2-react-grids';
import { isNullOrUndefined } from '@syncfusion/ej2-base';

interface sortInfo {
    name?: string;
    field?: string;
    direction: string
}

const initialSorting = { columns: [{ field: 'ProductID', direction: 'Descending' }] }
const initialParams = new Query().addParams('Syncfusion_React_Grid', 'true');
const initialPaging = { allowPaging: true };
const initialState: any = {
    data: lazyLoadData,
    error: false,
    result: [],
    count: 0,
}

const applySorting = (query: Query, sorted: sortInfo[]) => {
    if (sorted && sorted.length > 0) {
        sorted.forEach(sort => {
            const sortField = sort.name || sort.field;
            query.sortBy(sortField as string, sort.direction);
        });
    }
}

const applyPage = (query: Query, page: DataStateChangeEventArgs) => {
    if (page.take && page.skip) {
        const pageSkip = page.skip / page.take + 1;
        const pageTake = page.take;
        query.page(pageSkip, pageTake);
    }
    else if (page.skip === 0 && page.take) {
        query.page(1, page.take);
    }
}

const reducer = (state = initialState, action: any) => {
    createLazyLoadData();
    const dataSource = [...initialState.data];
    let query = new Query();
    switch (action.type) {
        case Grid_FetchData: {
            // sorting
            if (!isNullOrUndefined(action.payload.sorted)) {
                applySorting(query, action.payload.sorted);
            }

            // paging
            applyPage(query, action.payload);

            //addParams
            if (action.payload.params.length) {
                query.addParams('Syncfusion_React_Grid', 'true');
            }

            // To get the count of the data
            if (action.payload.requiresCounts) {
                query.isCountRequired = true;
            }
            const currentResult: any = new DataManager(dataSource).executeLocal(query);
            return ({
                data: {
                    result: currentResult.result,
                    count: currentResult.count
                } 
            })
        }

        default:
            if (initialSorting.columns.length) {
                applySorting(query, initialSorting.columns);
            }
            if (initialPaging.allowPaging) {
                const pageSkip = 1;
                const pageTake = 12;
                query.page(pageSkip, pageTake);
            }
            if (initialParams.params.length) {
                query.addParams('Syncfusion_React_Grid', 'true');
            }
            query.isCountRequired = true;
            const currentResult: any = new DataManager(dataSource).executeLocal(query);
            return ({
                data: { result: currentResult.result, count: currentResult.count }
            })
    }
}
export default reducer;
export const Grid_FetchData = "Grid_FetchData";
export const  fetchData= (state) => ({
    type: "Grid_FetchData",
    payload: state,
});
export const Grid_FetchData = "Grid_FetchData";
export const  fetchData= (state: any) => ({
    type: "Grid_FetchData",
    payload: state,
});
export let lazyLoadData = [];
export function createLazyLoadData() {
    if (lazyLoadData.length) {
        return;
    }
    let customerid = ['VINET', 'TOMSP', 'HANAR', 'VICTE', 'SUPRD', 'HANAR', 'CHOPS', 'RICSU', 'WELLI', 'HILAA',
        'ERNSH', 'CENTC', 'OTTIK', 'QUEDE', 'RATTC', 'ERNSH', 'FOLKO', 'BLONP', 'WARTH', 'FRANK', 'GROSR', 'WHITC', 'WARTH',
        'SPLIR', 'RATTC', 'QUICK', 'VINET', 'MAGAA', 'TORTU', 'MORGK', 'BERGS', 'LEHMS', 'BERGS', 'ROMEY', 'ROMEY', 'LILAS',
        'LEHMS', 'QUICK', 'QUICK', 'RICAR', 'REGGC', 'BSBEV', 'COMMI', 'QUEDE', 'TRADH', 'TORTU', 'RATTC', 'VINET', 'LILAS',
        'BLONP', 'HUNGO', 'RICAR', 'MAGAA', 'WANDK', 'SUPRD', 'GODOS', 'TORTU', 'OLDWO', 'ROMEY', 'LONEP', 'ANATR', 'HUNGO',
        'THEBI', 'DUMON', 'WANDK', 'QUICK', 'RATTC', 'ISLAT', 'RATTC', 'LONEP', 'ISLAT', 'TORTU', 'WARTH', 'ISLAT', 'PERIC',
        'KOENE', 'SAVEA', 'KOENE', 'BOLID', 'FOLKO', 'FURIB', 'SPLIR', 'LILAS', 'BONAP', 'MEREP', 'WARTH', 'VICTE',
        'HUNGO', 'PRINI', 'FRANK', 'OLDWO', 'MEREP', 'BONAP', 'SIMOB', 'FRANK', 'LEHMS', 'WHITC', 'QUICK', 'RATTC', 'FAMIA'];

    let product = ['Chai', 'Chang', 'Aniseed Syrup', 'Chef Anton\'s Cajun Seasoning', 'Chef Anton\'s Gumbo Mix',
        'Grandma\'s Boysenberry Spread', 'Uncle Bob\'s Organic Dried Pears', 'Northwoods Cranberry Sauce', 'Mishi Kobe Niku',
        'Ikura', 'Queso Cabrales', 'Queso Manchego La Pastora', 'Konbu', 'Tofu', 'Genen Shouyu', 'Pavlova', 'Alice Mutton',
        'Carnarvon Tigers', 'Teatime Chocolate Biscuits', 'Sir Rodney\'s Marmalade', 'Sir Rodney\'s Scones',
        'Gustaf\'s Knäckebröd', 'Tunnbröd', 'Guaraná Fantástica', 'NuNuCa Nuß-Nougat-Creme', 'Gumbär Gummibärchen',
        'Schoggi Schokolade', 'Rössle Sauerkraut', 'Thüringer Rostbratwurst', 'Nord-Ost Matjeshering', 'Gorgonzola Telino',
        'Mascarpone Fabioli', 'Geitost', 'Sasquatch Ale', 'Steeleye Stout', 'Inlagd Sill',
        'Gravad lax', 'Côte de Blaye', 'Chartreuse verte', 'Boston Crab Meat', 'Jack\'s New England Clam Chowder',
        'Singaporean Hokkien Fried Mee', 'Ipoh Coffee', 'Gula Malacca', 'Rogede sild', 'Spegesild', 'Zaanse koeken',
        'Chocolade', 'Maxilaku', 'Valkoinen suklaa', 'Manjimup Dried Apples', 'Filo Mix', 'Perth Pasties',
        'Tourtière', 'Pâté chinois', 'Gnocchi di nonna Alice', 'Ravioli Angelo', 'Escargots de Bourgogne',
        'Raclette Courdavault', 'Camembert Pierrot', 'Sirop d\'érable',
        'Tarte au sucre', 'Vegie-spread', 'Wimmers gute Semmelknödel', 'Louisiana Fiery Hot Pepper Sauce',
        'Louisiana Hot Spiced Okra', 'Laughing Lumberjack Lager', 'Scottish Longbreads',
        'Gudbrandsdalsost', 'Outback Lager', 'Flotemysost', 'Mozzarella di Giovanni', 'Röd Kaviar', 'Longlife Tofu',
        'Rhönbräu Klosterbier', 'Lakkalikööri', 'Original Frankfurter grüne Soße'];

    let customername = ['Maria', 'Ana Trujillo', 'Antonio Moreno', 'Thomas Hardy', 'Christina Berglund',
        'Hanna Moos', 'Frédérique Citeaux', 'Martín Sommer', 'Laurence Lebihan', 'Elizabeth Lincoln',
        'Victoria Ashworth', 'Patricio Simpson', 'Francisco Chang', 'Yang Wang', 'Pedro Afonso', 'Elizabeth Brown',
        'Sven Ottlieb', 'Janine Labrune', 'Ann Devon', 'Roland Mendel', 'Aria Cruz', 'Diego Roel',
        'Martine Rancé', 'Maria Larsson', 'Peter Franken', 'Carine Schmitt', 'Paolo Accorti', 'Lino Rodriguez',
        'Eduardo Saavedra', 'José Pedro Freyre', 'André Fonseca', 'Howard Snyder', 'Manuel Pereira',
        'Mario Pontes', 'Carlos Hernández', 'Yoshi Latimer', 'Patricia McKenna', 'Helen Bennett', 'Philip Cramer',
        'Daniel Tonini', 'Annette Roulet', 'Yoshi Tannamuri', 'John Steel', 'Renate Messner', 'Jaime Yorres',
        'Carlos González', 'Felipe Izquierdo', 'Fran Wilson', 'Giovanni Rovelli', 'Catherine Dewey', 'Jean Fresnière',
        'Alexander Feuer', 'Simon Crowther', 'Yvonne Moncada', 'Rene Phillips', 'Henriette Pfalzheim',
        'Marie Bertrand', 'Guillermo Fernández', 'Georg Pipps', 'Isabel de Castro', 'Bernardo Batista', 'Lúcia Carvalho',
        'Horst Kloss', 'Sergio Gutiérrez', 'Paula Wilson', 'Maurizio Moroni', 'Janete Limeira', 'Michael Holz',
        'Alejandra Camino', 'Jonas Bergulfsen', 'Jose Pavarotti', 'Hari Kumar', 'Jytte Petersen', 'Dominique Perrier',
        'Art Braunschweiger', 'Pascale Cartrain', 'Liz Nixon', 'Liu Wong', 'Karin Josephs', 'Miguel Angel Paolino',
        'Anabela Domingues', 'Helvetius Nagy', 'Palle Ibsen', 'Mary Saveley', 'Paul Henriot', 'Rita Müller',
        'Pirkko Koskitalo', 'Paula Parente', 'Karl Jablonski', 'Matti Karttunen', 'Zbyszek Piestrzeniewicz'];

    let customeraddress = ['507 - 20th Ave. E.\r\nApt. 2A', '908 W. Capital Way', '722 Moss Bay Blvd.',
        '4110 Old Redmond Rd.', '14 Garrett Hill', 'Coventry House\r\nMiner Rd.', 'Edgeham Hollow\r\nWinchester Way',
        '4726 - 11th Ave. N.E.', '7 Houndstooth Rd.', '59 rue de l\'Abbaye', 'Luisenstr. 48', '908 W. Capital Way',
        '722 Moss Bay Blvd.', '4110 Old Redmond Rd.', '14 Garrett Hill', 'Coventry House\r\nMiner Rd.',
        'Edgeham Hollow\r\nWinchester Way',
        '7 Houndstooth Rd.', '2817 Milton Dr.', 'Kirchgasse 6', 'Sierras de Granada 9993', 'Mehrheimerstr. 369',
        'Rua da Panificadora, 12', '2817 Milton Dr.', 'Mehrheimerstr. 369'];

    let quantityperunit = ['10 boxes x 20 bags', '24 - 12 oz bottles', '12 - 550 ml bottles',
        '48 - 6 oz jars', '36 boxes', '12 - 8 oz jars', '12 - 1 lb pkgs.', '12 - 12 oz jars',
        '18 - 500 g pkgs.', '12 - 200 ml jars',
        '1 kg pkg.', '10 - 500 g pkgs.', '2 kg box', '40 - 100 g pkgs.', '24 - 250 ml bottles', '32 - 500 g boxes',
        '20 - 1 kg tins', '16 kg pkg.', '10 boxes x 12 pieces', '30 gift boxes', '24 pkgs. x 4 pieces', '24 - 500 g pkgs.',
        '12 - 250 g pkgs.',
        '12 - 355 ml cans', '20 - 450 g glasses', '100 - 250 g bags'];

    let orderID = 10248;
    for (let i = 0; i < 20000; i++) {
        lazyLoadData.push({
            'OrderID': orderID + i,
            'CustomerID': customerid[Math.floor(Math.random() * customerid.length)],
            'CustomerName': customername[Math.floor(Math.random() * customername.length)],
            'CustomerAddress': customeraddress[Math.floor(Math.random() * customeraddress.length)],
            'ProductName': product[Math.floor(Math.random() * product.length)],
            'ProductID': i,
            'Quantity': quantityperunit[Math.floor(Math.random() * quantityperunit.length)]
        });
    }
}
export let lazyLoadData: Object[] = [];
export function createLazyLoadData(): void {
    if (lazyLoadData.length) {
        return;
    }
    let customerid: string[] = ['VINET', 'TOMSP', 'HANAR', 'VICTE', 'SUPRD', 'HANAR', 'CHOPS', 'RICSU', 'WELLI', 'HILAA',
        'ERNSH', 'CENTC', 'OTTIK', 'QUEDE', 'RATTC', 'ERNSH', 'FOLKO', 'BLONP', 'WARTH', 'FRANK', 'GROSR', 'WHITC', 'WARTH',
        'SPLIR', 'RATTC', 'QUICK', 'VINET', 'MAGAA', 'TORTU', 'MORGK', 'BERGS', 'LEHMS', 'BERGS', 'ROMEY', 'ROMEY', 'LILAS',
        'LEHMS', 'QUICK', 'QUICK', 'RICAR', 'REGGC', 'BSBEV', 'COMMI', 'QUEDE', 'TRADH', 'TORTU', 'RATTC', 'VINET', 'LILAS',
        'BLONP', 'HUNGO', 'RICAR', 'MAGAA', 'WANDK', 'SUPRD', 'GODOS', 'TORTU', 'OLDWO', 'ROMEY', 'LONEP', 'ANATR', 'HUNGO',
        'THEBI', 'DUMON', 'WANDK', 'QUICK', 'RATTC', 'ISLAT', 'RATTC', 'LONEP', 'ISLAT', 'TORTU', 'WARTH', 'ISLAT', 'PERIC',
        'KOENE', 'SAVEA', 'KOENE', 'BOLID', 'FOLKO', 'FURIB', 'SPLIR', 'LILAS', 'BONAP', 'MEREP', 'WARTH', 'VICTE',
        'HUNGO', 'PRINI', 'FRANK', 'OLDWO', 'MEREP', 'BONAP', 'SIMOB', 'FRANK', 'LEHMS', 'WHITC', 'QUICK', 'RATTC', 'FAMIA'];

    let product: string[] = ['Chai', 'Chang', 'Aniseed Syrup', 'Chef Anton\'s Cajun Seasoning', 'Chef Anton\'s Gumbo Mix',
        'Grandma\'s Boysenberry Spread', 'Uncle Bob\'s Organic Dried Pears', 'Northwoods Cranberry Sauce', 'Mishi Kobe Niku',
        'Ikura', 'Queso Cabrales', 'Queso Manchego La Pastora', 'Konbu', 'Tofu', 'Genen Shouyu', 'Pavlova', 'Alice Mutton',
        'Carnarvon Tigers', 'Teatime Chocolate Biscuits', 'Sir Rodney\'s Marmalade', 'Sir Rodney\'s Scones',
        'Gustaf\'s Knäckebröd', 'Tunnbröd', 'Guaraná Fantástica', 'NuNuCa Nuß-Nougat-Creme', 'Gumbär Gummibärchen',
        'Schoggi Schokolade', 'Rössle Sauerkraut', 'Thüringer Rostbratwurst', 'Nord-Ost Matjeshering', 'Gorgonzola Telino',
        'Mascarpone Fabioli', 'Geitost', 'Sasquatch Ale', 'Steeleye Stout', 'Inlagd Sill',
        'Gravad lax', 'Côte de Blaye', 'Chartreuse verte', 'Boston Crab Meat', 'Jack\'s New England Clam Chowder',
        'Singaporean Hokkien Fried Mee', 'Ipoh Coffee', 'Gula Malacca', 'Rogede sild', 'Spegesild', 'Zaanse koeken',
        'Chocolade', 'Maxilaku', 'Valkoinen suklaa', 'Manjimup Dried Apples', 'Filo Mix', 'Perth Pasties',
        'Tourtière', 'Pâté chinois', 'Gnocchi di nonna Alice', 'Ravioli Angelo', 'Escargots de Bourgogne',
        'Raclette Courdavault', 'Camembert Pierrot', 'Sirop d\'érable',
        'Tarte au sucre', 'Vegie-spread', 'Wimmers gute Semmelknödel', 'Louisiana Fiery Hot Pepper Sauce',
        'Louisiana Hot Spiced Okra', 'Laughing Lumberjack Lager', 'Scottish Longbreads',
        'Gudbrandsdalsost', 'Outback Lager', 'Flotemysost', 'Mozzarella di Giovanni', 'Röd Kaviar', 'Longlife Tofu',
        'Rhönbräu Klosterbier', 'Lakkalikööri', 'Original Frankfurter grüne Soße'];

    let customername: string[] = ['Maria', 'Ana Trujillo', 'Antonio Moreno', 'Thomas Hardy', 'Christina Berglund',
        'Hanna Moos', 'Frédérique Citeaux', 'Martín Sommer', 'Laurence Lebihan', 'Elizabeth Lincoln',
        'Victoria Ashworth', 'Patricio Simpson', 'Francisco Chang', 'Yang Wang', 'Pedro Afonso', 'Elizabeth Brown',
        'Sven Ottlieb', 'Janine Labrune', 'Ann Devon', 'Roland Mendel', 'Aria Cruz', 'Diego Roel',
        'Martine Rancé', 'Maria Larsson', 'Peter Franken', 'Carine Schmitt', 'Paolo Accorti', 'Lino Rodriguez',
        'Eduardo Saavedra', 'José Pedro Freyre', 'André Fonseca', 'Howard Snyder', 'Manuel Pereira',
        'Mario Pontes', 'Carlos Hernández', 'Yoshi Latimer', 'Patricia McKenna', 'Helen Bennett', 'Philip Cramer',
        'Daniel Tonini', 'Annette Roulet', 'Yoshi Tannamuri', 'John Steel', 'Renate Messner', 'Jaime Yorres',
        'Carlos González', 'Felipe Izquierdo', 'Fran Wilson', 'Giovanni Rovelli', 'Catherine Dewey', 'Jean Fresnière',
        'Alexander Feuer', 'Simon Crowther', 'Yvonne Moncada', 'Rene Phillips', 'Henriette Pfalzheim',
        'Marie Bertrand', 'Guillermo Fernández', 'Georg Pipps', 'Isabel de Castro', 'Bernardo Batista', 'Lúcia Carvalho',
        'Horst Kloss', 'Sergio Gutiérrez', 'Paula Wilson', 'Maurizio Moroni', 'Janete Limeira', 'Michael Holz',
        'Alejandra Camino', 'Jonas Bergulfsen', 'Jose Pavarotti', 'Hari Kumar', 'Jytte Petersen', 'Dominique Perrier',
        'Art Braunschweiger', 'Pascale Cartrain', 'Liz Nixon', 'Liu Wong', 'Karin Josephs', 'Miguel Angel Paolino',
        'Anabela Domingues', 'Helvetius Nagy', 'Palle Ibsen', 'Mary Saveley', 'Paul Henriot', 'Rita Müller',
        'Pirkko Koskitalo', 'Paula Parente', 'Karl Jablonski', 'Matti Karttunen', 'Zbyszek Piestrzeniewicz'];

    let customeraddress: string[] = ['507 - 20th Ave. E.\r\nApt. 2A', '908 W. Capital Way', '722 Moss Bay Blvd.',
        '4110 Old Redmond Rd.', '14 Garrett Hill', 'Coventry House\r\nMiner Rd.', 'Edgeham Hollow\r\nWinchester Way',
        '4726 - 11th Ave. N.E.', '7 Houndstooth Rd.', '59 rue de l\'Abbaye', 'Luisenstr. 48', '908 W. Capital Way',
        '722 Moss Bay Blvd.', '4110 Old Redmond Rd.', '14 Garrett Hill', 'Coventry House\r\nMiner Rd.',
        'Edgeham Hollow\r\nWinchester Way',
        '7 Houndstooth Rd.', '2817 Milton Dr.', 'Kirchgasse 6', 'Sierras de Granada 9993', 'Mehrheimerstr. 369',
        'Rua da Panificadora, 12', '2817 Milton Dr.', 'Mehrheimerstr. 369'];

    let quantityperunit: string[] = ['10 boxes x 20 bags', '24 - 12 oz bottles', '12 - 550 ml bottles',
        '48 - 6 oz jars', '36 boxes', '12 - 8 oz jars', '12 - 1 lb pkgs.', '12 - 12 oz jars',
        '18 - 500 g pkgs.', '12 - 200 ml jars',
        '1 kg pkg.', '10 - 500 g pkgs.', '2 kg box', '40 - 100 g pkgs.', '24 - 250 ml bottles', '32 - 500 g boxes',
        '20 - 1 kg tins', '16 kg pkg.', '10 boxes x 12 pieces', '30 gift boxes', '24 pkgs. x 4 pieces', '24 - 500 g pkgs.',
        '12 - 250 g pkgs.',
        '12 - 355 ml cans', '20 - 450 g glasses', '100 - 250 g bags'];

    let orderID: number = 10248;
    for (let i: number = 0; i < 20000; i++) {
        lazyLoadData.push({
            'OrderID': orderID + i,
            'CustomerID': customerid[Math.floor(Math.random() * customerid.length)],
            'CustomerName': customername[Math.floor(Math.random() * customername.length)],
            'CustomerAddress': customeraddress[Math.floor(Math.random() * customeraddress.length)],
            'ProductName': product[Math.floor(Math.random() * product.length)],
            'ProductID': i,
            'Quantity': quantityperunit[Math.floor(Math.random() * quantityperunit.length)]
        });
    }
}

AdditionalParameters

Offline mode

On remote data binding, all grid actions such as paging, sorting, editing, grouping, filtering, etc, will be processed on server-side. To avoid post back for every action, set the grid to load all data on initialization and make the actions process in client-side. To enable this behavior, use the offline property of DataManager.

import { ColumnDirective, ColumnsDirective, GridComponent, PageSettingsModel, Page, Group, Sort } from '@syncfusion/ej2-react-grids';
import { DataManager, ODataAdaptor } from '@syncfusion/ej2-data';
import * as React from 'react';

function App() {
    const pageSettings: PageSettingsModel = { pageSize: 7 };
    const data: DataManager = new DataManager({
      url: 'https://js.syncfusion.com/demos/ejServices/Wcf/Northwind.svc/Orders?$top=7',
      adaptor: new ODataAdaptor(),
      offline: true
    });
    return <div>
        <GridComponent dataSource={data} height={315} allowPaging={true} pageSettings={pageSettings} allowSorting={true} allowGrouping={true}>
            <ColumnsDirective>
                <ColumnDirective field='OrderID' headerText='Order ID' width='120' textAlign="Right" />
                <ColumnDirective field='CustomerID' headerText='Customer ID' width='150' />
                <ColumnDirective field='ShipCity' headerText='Ship City' width='150' />
                <ColumnDirective field='ShipName' headerText='Ship Name' width='150' />
            </ColumnsDirective>
            <Inject Services={[Page, Sort, Group]}/>
        </GridComponent>
    </div>
};
export default App;