How to Perform CRUD Actions in a React Data Grid Using Redux: A Complete Guide
Tags: react, webdev, crud, redux
Published: 2021-09-12 04:10:31

---
title: How to Perform CRUD Actions in a React Data Grid Using Redux: A Complete Guide
published: true
date: 2021-09-08 11:54:52 UTC
tags: react, webdev, crud, redux
canonical_url: https://www.syncfusion.com/blogs/post/how-to-perform-crud-actions-in-a-react-data-grid-using-redux-a-complete-guide.aspx
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0qr8m087g7yqeb9agpla.png
---

The Data Grid is one of the most useful controls, representing data simply and interactively. It allows you to display data in rows and columns. Each cell may contain text, images, or other controls. From simple contact lists to complex grids, Data Grid can handle it all.

Our Syncfusion [React DataGrid](https://www.syncfusion.com/react-ui-components/react-data-grid "Link to React DataGrid") is a feature-rich control for displaying data in a tabular format. Its functionalities include data binding, editing, Excel-like filtering, selection, and more. It also supports exporting data to Excel, CSV, and PDF formats.

In this blog, we will see how to connect your React application with the Syncfusion React Data Grid and perform CRUD actions using [Redux](https://redux.js.org/introduction/getting-started "Link to Getting Started with Redux") with and without [middleware](https://en.wikipedia.org/wiki/Middleware "Link to the Wikipedia page for Middleware").

The contents of this blog are as follows:

- [Create a React app and integrate the Syncfusion React Data Grid](#create-a-react-app-and-integrate-syncfusion-react-datagrid).
- [Module injection](#module-injection).
- [Add Redux service](#add-redux-service-in-your-application).
- [Update the app state without using middleware in Redux and perform CRUD](#update-the-app-state-using-redux-without-middleware).
- [Update the app state using middleware in Redux and perform CRUD](#update-the-app-state-using-redux-with-middleware).

Let’s get started!

## Create a React application and integrate the Syncfusion React Data Grid

### Step 1:  Create a new React application.

First, Install the [create-react-app](https://www.npmjs.com/package/create-react-app "Link to create-react-app npm package") npm package using the following command in the desired location.

| `npx create-react-app my-app` |

**Note:** Refer to the [Getting Started with React Apps documentation](https://create-react-app.dev/docs/getting-started/ "Link to Getting Started with React App documentation") to create a React app using the **npm** and **yarn** commands.

### Step 2: Add the Syncfusion Packages.

All the available Essential JS 2 packages are published in the [npmjs.com ](https://www.npmjs.com/~syncfusionorg "Link to Syncfusion packages on npm")public registry. Use the following command to install the React DataGrid component.

| `npm install @syncfusion/ej2-react-grids --save` |

### Step 3: Add the CSS reference for the Syncfusion Data Grid component.

The following CSS files are available in the  **../node\_modules/@syncfusion**  package folder. Add them as references in the  **src/App.css file**.

```js
@import '../node_modules/@syncfusion/ej2-base/styles/material.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
@import '../node_modules/@syncfusion/ej2-calendars/styles/material.css';
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/material.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/material.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css';
@import "../node_modules/@syncfusion/ej2-react-grids/styles/material.css";
```

**Note:** For more details, refer to the [Getting started with React Data Grid documentation](https://ej2.syncfusion.com/react/documentation/grid/getting-started/#getting-started "Link to Getting started with React Grid component").

### Step 4: Add the Syncfusion Data Grid to your application.

Now, place the following code in the  **src/App.js** file to add the Syncfusion Data Grid component.

```js
import * as React from 'react';
import './App.css';
import { ColumnDirective, ColumnsDirective, GridComponent, Inject} from '@syncfusion/ej2-react-grids';

class App extends React.Component {

    render() {
        // Render the EJ2 Grid component. 
        return <GridComponent ref={grid => this.gridInstance = grid} >
            <ColumnsDirective>
                <ColumnDirective field='OrderID' headerText='Order ID' width='140' textAlign='Right' ></ColumnDirective>
                <ColumnDirective field='CustomerID' headerText='Customer Name' width='150' ></ColumnDirective>
                <ColumnDirective field='Freight' headerText='Freight' width='140' format='C2' textAlign='Right' ></ColumnDirective>
                <ColumnDirective field='ShipCountry' headerText='Ship Country' width='150' ></ColumnDirective>
            </ColumnsDirective>
            
        </GridComponent>
    }
};

export default App;
```

**Note:** The columns are automatically generated when the columns declaration is empty or undefined while initializing the grid. All the columns in the data source are bound to grid columns. At that same time, the data source should not be empty at the initial loading. Since columns are generated from the data source in the autogenerated column grid. So, we should define the **columns** and **DataSource** properties for grid rendering.

## Module injection

The React Data Grid’s features are segregated into individual modules by feature. To use a particular feature, you need to inject its feature service into the app.

Here, we are going to use the paging, sorting, filtering, editing, and toolbar features of the React Data Grid. The relevant features’ service names and descriptions are as follows:

- **Page** : For paging feature.
- **Sort** : For sorting feature.
- **Filter** : For filtering feature.
- **Edit** : For editing feature.
- **Toolbar** : For toolbar feature.

**Note:** For more details, refer to the [Feature Modules in the React Data Grid documentation](https://ej2.syncfusion.com/react/documentation/grid/module/ "Link to Feature Modules in React Grid component").

Now, let’s inject these modules into the grid using the  **Inject**  directive in the App.js file.

```js
import * as React from 'react';
import './App.css';
import { ColumnDirective, Edit, ColumnsDirective, Filter, GridComponent, Inject, Page, Sort, Toolbar } from '@syncfusion/ej2-react-grids';

class App extends React.Component {    
    pageSettings = { pageSize: 6 };
    validationRule = { required: true };
    orderidRules = { required: true, number: true };
    editOptions = { allowEditing: true, allowAdding: true, allowDeleting: true };
    toolbarOptions = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
    render() {
        // Render the EJ2 Grid component. 
        return <GridComponent ref={grid => this.gridInstance = grid} dataSource={this.props.data1.currentData} allowSorting={true} editSettings={this.editOptions} toolbar={this.toolbarOptions} allowFiltering={true} allowPaging={true}>
            <ColumnsDirective>
                <ColumnDirective field='OrderID' headerText='Order ID' width='140' textAlign='Right' validationRules={this.orderidRules} isPrimaryKey={true}></ColumnDirective>
                <ColumnDirective field='CustomerID' headerText='Customer Name' width='150' validationRules={this.validationRule}></ColumnDirective>
                <ColumnDirective field='Freight' headerText='Freight' width='140' format='C2' textAlign='Right' editType='numericedit' ></ColumnDirective>
                <ColumnDirective field='ShipCountry' headerText='Ship Country' width='150' ></ColumnDirective>
            </ColumnsDirective>
            <Inject services={[Page, Sort, Filter, Edit, Toolbar]} />
        </GridComponent>
    }
};

export default App;
```

**Note:**  The editing feature requires a primary key column to perform CRUD operations. The primary key should be a unique value. To define the primary key, set the [columns.isPrimaryKey](https://ej2.syncfusion.com/react/documentation/api/grid/column/#isprimarykey "Link to the isPrimaryKey property of React DataGrid") to  **true**  in the required column.

Now, we have successfully integrated the Syncfusion React Data Grid into your app. Let’s learn how to fetch and bind data to the Data Grid, as well as perform CRUD operations using the Redux service.

## Add Redux service to your application

Redux is a Javascript library for managing the global state of your application. It helps us to maintain the state of an entire application in a single immutable state object. We can’t change the state value directly. But we can update the state changes by using actions and reducers that return a new state object.

Install the Redux service in your application with the following command.

| `npm install redux` |

Then, add the redux package to your application using the following command.

| `npm install --save redux` |

**Note:** For more details, refer to the [Redux Toolkit Installation documentation](https://redux.js.org/introduction/installation "Link to Redux Toolkit Installation documentation").

Now, we can update the application state by using the Redux service with or without using middleware.

## Update the app state without using middleware in Redux

The following screenshot will explain the flow of updating an application state without using middleware in Redux.

![Flowchart for updating the app state without using middleware in Redux](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Flowchart-for-updating-the-app-state-without-using-middleware-in-Redux-1.jpg)

### Integrating Redux service in your application

Redux has the following important principles:

- **Single source of truth** : Redux helps us create universal apps. The state of the application is stored in one object tree called the store. It means Redux has only one store and that can have only one app.
- **State is read-only (immutability):** We can’t change the state object or its properties directly. Instead, we must make a new object, renew the app state, and update it with the newly created object. This is important because all the changes are happening in the same place, so everything needs to be done in sequential order.
- **Changes are made with pure functions (reducers):** Reducers are pure functions that will take the previous state and action (discuss later) and return the new state.

**Redux traversing to update state**

![Redux traversing to update state](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Redux-traversing-to-update-state.jpg) **Folder structure of the application**

![Folder structure of the application](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Folder-structure-of-the-application.jpg)

### Action and its types

The **Action** is an event that will be indicated in your backend when you perform any action in your app. Using the **action.type** , we can determine which sort of action is triggered by the application.

The **action.type** defines the name of the action that will be performed from your application. The  **action.type** should be defined as string constants.

**Note:** For further information, refer to [Designing Actions documentation](https://redux.js.org/tutorials/fundamentals/part-3-state-actions-reducers#designing-actions "Link to Designing Actions in Redux documentation").

In this demo, we have used the following action types, defined in the **action.js** file in the path location **_src/reducer/action.js_**.

**[action.js]**

```js
export const Grid_Paging ="Grid_Paging";
export const Grid_Sorting = "Grid_Sorting";
export const Grid_Filtering = "Grid_Filtering";
export const Grid_Add = "Grid_Add";
export const Grid_Editing = "Grid_Editing";
export const Grid_Delete = "Grid_Delete";
```

### Reducer

**Reducer** is a pure function that takes your application’s current state and returns a new state for it.

For further information, refer to [Writing Reducers documentation](https://redux.js.org/tutorials/fundamentals/part-3-state-actions-reducers#writing-reducers "Link to Writing Reducers in Redux").

In this demo, we have defined the reducer in the **reducer.js** file in the path location **src/reducer/reducer.js**.

**[reducer.js]**

```js
import {
    Grid_Paging,Grid_Sorting,Grid_Filtering,Grid_Add,
Grid_Editing,Grid_Delete} from './action';

const initialState = {
    error: false,
    result: [],
    count: 0,
    isUpdated: false
}
const reducer = (state = initialState, action) => {
. . .
    switch (action.type) {
        case Grid_Paging: {
            . . .
         return data
          . . .
    }
  }
}
```

### Store

The **Store** holds the state of the entire application. Redux can have only a single store in an application. Whenever a store is created in Redux, you need to specify the reducer.

Let’s see how we can create a store using the  **createStore**  method from Redux. To do so, we need to import the **createStore** module from the **_redux_** library.

For further information, refer to the [Redux Store documentation](https://redux.js.org/tutorials/fundamentals/part-4-store#redux-store "Link to Redux Store").

In this demo, we have created the store in the **store.js** file in the path location   **src/store/store.js.**

**[store.js]**

```js
import { createStore } from 'redux';
import reducer from "../reducer/reducer";// Import your reducer.
const store = new createStore(reducer);//Give name for your reducer. 
export default store;
```

In this demo, we have defined the reducer name as **reducer** and specified it in the previous store.

### Provider

The **<Provider>** component makes the Redux store available to any nested component that needs to access it. Refer to the [Provider API](https://react-redux.js.org/introduction/getting-started#provider "Link to getting started with Provider API in Redux") for more details.

You can import the **<Provider>** component from the react-redux library by installing the redux in your application.

| `npm install --save redux react-redux` |

Then, add the **<Provider>** component in your application using the following code.

**[index.js]**

```js
import { React } from 'react';
import { Provider } from 'react-redux'
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import store from './store/store'

ReactDOM.render(

    <Provider store={store}>
        <App />
    </Provider>,

    document.getElementById('root')
);
```

### Connect

To connect the React Data Grid to the Redux store, use the Redux [_connect()_](https://react-redux.js.org/api/connect "Link to connect method of Redux") method. We can use the [_mapStateToProps_](https://react-redux.js.org/using-react-redux/connect-mapstate "Link to mapStateToProps property of redux") and [_mapDispatchToProps_](https://react-redux.js.org/using-react-redux/connect-mapdispatch "Link to mapDispatchToProps properties of Redux") properties to dispatch the app action and get the updated value.

First, import **_connect_** from the **_redux_** library to use the **_connect()_** function in your application.

| `import { connect } from 'react-redux';` |

Then, include the following code in the App.js file.

**[App.js]**

```js
import { connect } from 'react-redux';
 . . .
render() {
    // Render the EJ2 Grid component. 
    return <GridComponent ref={grid => this.gridInstance = grid} dataSource={this.props.data1.currentData} allowSorting={true} editSettings={this.editOptions} toolbar={this.toolbarOptions} allowFiltering={true} allowPaging={true}>
        <ColumnsDirective>
           . . .
    </GridComponent>
}
};
const mapStateToProps = (state, props) => {
// UpdateData is a reselect selector.
    return { data1: UpdateData(state) }
}

const mapDispatchToProps = dispatch => {
    return {
        dispatch
    }
}

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(App);
```

### CreateSelector

In this demo, we are going to use the **_CreateSelector_** method from the **_reselect_** library. The _[createSelector](https://redux-toolkit.js.org/api/createSelector "Link to createSelector")_ is a memorized selector. It only recalculates when a part of the state tree changes. The value of the selector doesn’t change when there is a change in other (unrelated) parts of the state tree.

Use the following command to install the _[reselect library](https://www.npmjs.com/package/reselect "Link to reselect npm package")_ in your application.

| `npm i reselect` |

In this demo, we have imported and defined the CreateSelector in the selector.js file in the path location **src/selector.js**.

```js
import { createSelector } from 'reselect';
const gData = state => state;
const UpdateData = createSelector(
    [gData], (data1) => {
        return { currentData: data1.data, isUpdated: data1.isUpdated };
    }
)
export default UpdateData;
```

### Fetch data through Redux service using the Data Grid

If you wish to get data through the Redux service, use the [Custom DataBinding](https://ej2.syncfusion.com/react/documentation/grid/data-binding/#custom-binding "Link to Custom Binding in React Data Grid") feature in the React Data Grid. With this feature, you can get data from your service on-demand. So, that the Data Grid loads the records of only the current page (i.e., it does not load all the data at once). You should return the result (the records from the current page ) and count (the total number of records in your data source) when enabling **Custom-DataBinding** in your app.

The Data Grid uses the following events to invoke actions in  **_Custom-DataBinding_** :

- [_dataStateChange_](https://ej2.syncfusion.com/react/documentation/api/grid/#datastatechange "Link to dataStateChange property of React DataGrid"): To handle the Data Grid actions like paging, sorting, and filtering.
- [dataSourceChanged](https://ej2.syncfusion.com/react/documentation/api/grid/#datasourcechanged "Link to dataSourceChanged property of React DataGrid") **: ** To handle CRUD actions.

**Note:** During initial rendering, the [dataStateChange](https://ej2.syncfusion.com/react/documentation/api/grid/#datastatechange "Link to dataStateChange property of React DataGrid") event will not be triggered. You can perform the operation in the componentDidMount when you want to show a record in the grid.

#### Data fetching

We have already explained [how to add the Syncfusion DataGrid component to your application](#add-syncfusion-datagrid-in-your-app). We are using the same code here.

**Note:** In this demo, we’ll use the Syncfusion DataManager library to run the grid query and get the results for all the grid actions. You may also apply your logic based on your database.

**[App.js]**

```js
import * as React from 'react';
import './App.css';
import { ColumnDirective, Edit, ColumnsDirective, Filter, GridComponent, Inject, Page, Sort, Toolbar } from '@syncfusion/ej2-react-grids';

class App extends React.Component {    
    pageSettings = { pageSize: 6 };
    render() {
        // Render the EJ2 Grid component. 
        return <GridComponent ref={grid => this.gridInstance = grid} dataSource={this.props.data1.currentData} dataStateChange={this.dataStateChange.bind(this)} dataSourceChanged={this.dataSourceChanged.bind(this)} allowSorting={true} allowFiltering={true} allowPaging={true}>
            <ColumnsDirective>
                <ColumnDirective field='OrderID' headerText='Order ID' width='140' textAlign='Right' validationRules={this.orderidRules} isPrimaryKey={true}></ColumnDirective>
                <ColumnDirective field='CustomerID' headerText='Customer Name' width='150' validationRules={this.validationRule}></ColumnDirective>
                <ColumnDirective field='Freight' headerText='Freight' width='140' format='C2' textAlign='Right' editType='numericedit' ></ColumnDirective>
                <ColumnDirective field='ShipCountry' headerText='Ship Country' width='150' ></ColumnDirective>
            </ColumnsDirective>
            <Inject services={[Page, Sort, Filter]} />
        </GridComponent>
    }
};

export default App;
```

The [_dataStateChange_](https://ej2.syncfusion.com/react/documentation/api/grid/#datastatechange "Link to dataStateChange property of React DataGrid") event will not be triggered at initial loading. So, you need to set the initial parameter based on your [pageSize](https://ej2.syncfusion.com/react/documentation/api/grid/pageSettingsModel/#pagesize "Link to pageSize property of React DataGrid") and initial settings.

```js
import { orderDetails } from '../data';
import { DataManager, Query } from '@syncfusion/ej2-data';
import {
    Grid_Paging, Grid_Sorting, Grid_Filtering,
    Grid_Add, Grid_Editing, Grid_Delete
} from './action';

// Initially the grid dataStateChange event is not triggered. Here, we set default pageSize as take… 
//you can also set pageSize based on your application. 
const initialPage = { skip: 0, take: 12 }
const initialState = {
    data: orderDetails,
    error: false,
    result: [],
    count: 0,
    isUpdated: false
}

const reducer = (state = initialState, action, gquery) => {
    const dataSource = [...initialState.data];
    let filter = [];
    const gData = new DataManager(dataSource);

    // we execute the Data Grid action and perform CRUD by using the DataManager. 
    //You can execute a query based on your server.
    switch (action.type) {
        case Grid_Paging: {
        . . .
        }
        . . . 
        default: {
            // We need to return the grid data as result and count with object type.
            const count1 = state.data.length;
            const data1 = { data: { result: state.data.slice(initialPage.skip, initialPage.take), count: count1 }, isUpdated: false }
            return data1;
        }
    }
}

export default reducer;
```

In the previous code, we set the **_skip_** and **_take_** values based on the Data Grid default **_pageSize_** value (12 is the default value).  Also, we can fetch the current page records and the total number of records from your dataSource using the **_result_** and **_count_** formats, respectively.

### Paging

The paging action will trigger the **_dataStateChange_** event and return the **_skip_** and **_take_** values of the next page. Let’s dispatch the paging action to the reducer using the following code.

**[APP.js]**

```js
 . . .
dataStateChange(args) {
    // This event will be triggered when performing any grid action.

    const query = this.gridInstance.getDataModule().generateQuery(true); // Get the Grid query without pagination.

    // Dispatch the page query.
    if (args.action.requestType == "paging" || args.action.requestType == "refresh" || args.action.requestType == "delete") {
        this.props.dispatch({
            type: Grid_Paging,
            payload: args,
            gQuery: query

        })
    }
...
```

Now, you will get the query string like in the following screenshot,

![Query string for paging](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Query-string-for-pagination.jpg)Here, we are going to use the Syncfusion DataManager library to run the Data Grid query and get the results. Also, you can apply your logic based on your database.

**[reducer.js]**

```js
const reducer = (state = initialState, action, gquery) => {
    const dataSource = [...initialState.data];
    let filter = [];
    const gData = new DataManager(dataSource);
    if (action.gQuery !== undefined) {
        filter = action.gQuery.queries.filter((fn, i) => {
            return fn.fn == "onWhere"
        })
    }

    // We are going to execute the grid action and perform CRUD using the DataManager.. 
    //You can execute a query based on your server.
    switch (action.type) {
        case Grid_Paging: {

            // Here, we have executed the grid query by using DataManager.
            const cData = gData.executeLocal(new Query());
            // Execute the grid query except pagination....
            const gridData = action.gQuery !== undefined ? new DataManager(cData).executeLocal(action.gQuery) : cData;
            // Execute the Grid page query... based on your skip and take values.
            const currentPageData = new DataManager(gridData).executeLocal(new Query().skip(action.payload.skip).take(action.payload.take));

            // We need to return the Data Grid data as result and count with the object type.
            return ({

                data: { result: currentPageData, count: filter.length ? gridData.length : cData.length }, isUpdated: false
            })
        }
```

Then, we need to return the **_result_** and **_count_** formats.

### Filtering and sorting

Similarly, the _ **dataStateChange** _ event is triggered when performing the sorting and filtering actions. It returns the query based on your actions.

Refer to the following code example to dispatch the Data Grid filtering and sorting actions to the reducer.

```js
 . . .
dataStateChange(args) {
    // This event will be triggered when performing any grid action.

    const query = this.gridInstance.getDataModule().generateQuery(true); // Get the Grid query without pagination.

    // Dispatch the page query.
    if (args.action.requestType == "paging" || args.action.requestType == "refresh" || args.action.requestType == "delete") {
        this.props.dispatch({
            type: Grid_Paging,
            payload: args,
            gQuery: query

        })
    }
    // Dispatch the filtering query.
    if (args.action.requestType == 'filtering') {
        this.props.dispatch({
            type: Grid_Filtering,
            payload: args,
            gQuery: query
        })
    }
    // Dispatch the sorting query.
    if (args.action.requestType == 'sorting') {
        this.props.dispatch({
            type: Grid_Sorting,
            payload: args,
            gQuery: query
        })
    }
 . . .
```

**[reducer.js]**

```js
const reducer = (state = initialState, action, gquery) => {
    const dataSource = [...initialState.data];
    let filter = [];
    const gData = new DataManager(dataSource);
    if (action.gQuery !== undefined) {
        filter = action.gQuery.queries.filter((fn, i) => {
            return fn.fn == "onWhere"
        })
    }

    // We are going to execute the grid action and perform CRUD action by using the DataManager.. 
    //You can execute a query based on your server.
    switch (action.type) {
        case Grid_Paging: {
              . . .
            
        case Grid_Filtering: {
            // Here, we have executed the grid query by using the DataManager.
            const flData = gData.executeLocal(action.gQuery);
            // Execute the grid page query... based on your skip and take values.
            const currentPageData = new DataManager(flData).executeLocal(new Query().skip(action.payload.skip).take(action.payload.take))
            return ({
                data: { result: currentPageData, count: flData.length }, isUpdated: false
            })
        }
        case Grid_Sorting: {
            // Execute the grid sort query... .
            const sortData = gData.executeLocal(action.gQuery);
            // Execute the grid page query... based on your skip and take values.
            const currentPageData = new DataManager(sortData).executeLocal(new Query().skip(action.payload.skip).take(action.payload.take));
            // We need to return the Grid data as result and count with object type.
            return ({
                data: { result: currentPageData, count: sortData.length }, isUpdated: false
            })
        }
        
. . .
    }
}

export default reducer;
```

Find the requests sent to your data service in the following screenshots.

**_Sorting request_**

_![Sorting request](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Sorting-request-1.jpg)_ **_Filtering request_**

![Filtering request](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Filtering-request-1.jpg)

### CRUD

The React Data Grid has options to dynamically insert, delete and update records. The editing feature requires a primary key column to perform CRUD operations. To define the primary key, set the [_columns.isPrimaryKey_](https://ej2.syncfusion.com/react/documentation/api/grid/column/#isprimarykey "Link to isPrimaryKey property of React DataGrid") to true in the required column.

You can start the edit action either by double-clicking the particular row or by selecting the required row and clicking on the Edit button in the toolbar. Similarly, you can add a new record to the grid by clicking either the Add button in the toolbar or on an external button bound to invoke the [addRecord](https://ej2.syncfusion.com/react/documentation/api/grid/#addrecord "Link to addRecord property of React DataGrid") method of the grid. The save and cancel actions in the edit mode are possible using their toolbar icons in the grid. To delete a record, select the required record and click on the Delete button in the toolbar.

To perform CRUD actions, inject the Edit module in the React Data Grid. The _[dataSourceChanged](https://ej2.syncfusion.com/react/documentation/api/grid/#datasourcechanged "Link to dataSourceChanged property of React DataGrid")_ event will be triggered when you perform any CRUD action. (This event will be triggered only when we use the custom-binding feature.)

```js
import * as React from 'react';
import './App.css';
import { ColumnDirective, Edit, ColumnsDirective, Filter, GridComponent, Inject, Page, Sort, Toolbar } from '@syncfusion/ej2-react-grids';

class App extends React.Component {    
    pageSettings = { pageSize: 6 };
    validationRule = { required: true };
    orderidRules = { required: true, number: true };
    editOptions = { allowEditing: true, allowAdding: true, allowDeleting: true };
    toolbarOptions = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
    render() {
        // Render the EJ2 Grid component. 
        return <GridComponent ref={grid => this.gridInstance = grid} dataSource={this.props.data1.currentData} dataStateChange={this.dataStateChange.bind(this)} dataSourceChanged={this.dataSourceChanged.bind(this)} allowSorting={true} editSettings={this.editOptions} toolbar={this.toolbarOptions} allowFiltering={true} allowPaging={true}>
            <ColumnsDirective>
                <ColumnDirective field='OrderID' headerText='Order ID' width='140' textAlign='Right' validationRules={this.orderidRules} isPrimaryKey={true}></ColumnDirective>
                <ColumnDirective field='CustomerID' headerText='Customer Name' width='150' validationRules={this.validationRule}></ColumnDirective>
                <ColumnDirective field='Freight' headerText='Freight' width='140' format='C2' textAlign='Right' editType='numericedit' ></ColumnDirective>
                <ColumnDirective field='ShipCountry' headerText='Ship Country' width='150' ></ColumnDirective>
            </ColumnsDirective>
            <Inject services={[Page, Sort, Filter, Edit, Toolbar]} />
        </GridComponent>
    }
};

export default App;
```

#### Update

Use update method to modify a record in the data source. In custom data binding, the modified record will be sent along with the request to your data service. Refer to the following code for dispatching the update action to the reducer.

**[App.js]**

```js
dataSourceChanged(state) {

    // This event will be triggered when we perform a CRUD action.

    this.gState = Object.assign(this.gState, state); // Store the Grid async process.

    const query = this.gridInstance.getDataModule().generateQuery(true); // Get the Grid query without pagination.

    // Dispatch the editing action.
    if (state.action == 'edit') {
        this.props.dispatch({
            type: Grid_Editing,
            payload: state,
            gQuery: query
        })
    }
 . . .
```

**[reducer.js]**

We are going to use the **_isUpdated_** property, to apply changes in your database and return this property from the reducer.

```js
const reducer = (state = initialState, action, gquery) => {
 . . .
    switch (action.type) {
. . .
        case Grid_Editing: {
            // Here, we are going to perform insert action by using the DataManager.
            gData.update('OrderID', action.payload.data);
            const updatedData = gData.executeLocal(new Query());
            initialState.data = [...updatedData];
            const count = updatedData.length;
            const gridData = new DataManager(updatedData).executeLocal(action.gQuery);
            // Execute the grid page query... based on your skip and take values.
            const currentPageData = new DataManager(gridData).executeLocal(new Query().skip(action.payload.state.skip).take(action.payload.state.take));

            // We need to return the grid data as result and count with object type.
            return ({
                data: { result: currentPageData, count: filter.length ? gridData.length : count }, isUpdated: true,
            })
        }
 . . .
```

You can get the modified data in the data parameters. Refer to the following screenshot for updating requests.

![Update request](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Update-request.jpg)The **_dataSourceChanged_** event is an asynchronous process. So, you need to call the following method to end the **_dataSourceChanged_** process and refresh the Data Grid.

Call the **_endEdit()_** method to complete the **_dataSourceChanged_** event process in the **_componentDidUpdate_** lifecycle method of React.

```js
componentDidUpdate(prevProps, prevState, snapshot) {
    if (this.props.data1.isUpdated) {
        this.gState.endEdit(); // To complete the Grid CRUD - async process. 
        
    }
}
```

Now, we have successfully applied the changes. Refer to the following screenshot.

![Updating data in React Data Grid](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Updating-data-in-React-Data-Grid.jpg)

#### Insert

Use insert action to add new records to the data source. In the custom data binding, the new record will be sent along with the request to your data service. Refer to the following code to dispatch the insert action to the reducer.

**[App.js]**

```js
dataSourceChanged(state) {

    // This event will be triggered when you perform any CRUD action.

    this.gState = Object.assign(this.gState, state); // Store the Data Grid async process.

    const query = this.gridInstance.getDataModule().generateQuery(true); // Get the Grid query without pagination.

. . .

    // Dispatch the insert action.
    else if (state.action == 'add') {
        this.props.dispatch({
            type: Grid_Add,
            payload: state,
            gQuery: query
        })
    }
    . . .
    }
}
```

**[reducer.js]**

We are going to return the **_isUpdated_** property from the reducer and use this property to apply changes in your database.

```js
const reducer = (state = initialState, action, gquery) => {
    
    . . .

    // We are going to execute the grid action and perform CRUD by using the DataManager. 
    //you can execute a query based on your server.
    switch (action.type) {
        . . .
        case Grid_Add: {
            // Here, we will perform the insert action using the DataManager.
            gData.insert(action.payload.data, '', null, 0);
            const addedData = gData.executeLocal(new Query());
            // Update the original state.
            initialState.data = [...addedData];
            const count = addedData.length;
            const gridData = new DataManager(addedData).executeLocal(action.gQuery);
            // Execute the grid page query... based on your skip and take values.
            const currentPageData = new DataManager(gridData).executeLocal(new Query().skip(action.payload.state.skip).take(action.payload.state.take));
            // We need to return the grid data as result and count with object type.
            return ({
                data: { result: currentPageData, count: filter.length ? gridData.length : count }, isUpdated: true,
            })
        }
 . . .
```

You can get the new data from the data parameters. Refer to the following screenshot for the inserting request.

![Insert request](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Insert-request.jpg)As I already said, the **_dataSourceChanged_** event is an asynchronous process. So, call the **_endEdit()_**  method to end the **_dataSourceChanged_** process in the **_componentDidUpdate_** lifecycle method of React and refresh the Data Grid.

```js
componentDidUpdate(prevProps, prevState, snapshot) {
    if (this.props.data1.isUpdated) {
        this.gState.endEdit(); // To complete Grid CRUD - async process. 
        
    }
}
```

Now, we have successfully added the new record and updated the pager item, too.

#### ![Inserting data in React DataGrid](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Inserting-data-in-React-DataGrid.jpg)Delete

Use the remove method to remove a record from the data source. In the custom data binding, the record details such as primary key and data will be sent along with the request to your data service. Refer to the following code to dispatch the delete action to the reducer.

**[App.js]**

```js
dataSourceChanged(state) {

. . .

    // Dispatch the delete action.
    else if (state.requestType == 'delete') {
        this.props.dispatch({
            type: Grid_Delete,
            payload: state,
            gQuery: query
        })
    }
```

**[reducer.js]**

We are going to return the **_isUpdated_** property from the reducer and use this property to apply changes in your database.

```js
const reducer = (state = initialState, action, gquery) => {
. . .

    // We are going to execute the grid action and perform CRUD by using the DataManager.
    //You can execute a query based on your server.
    switch (action.type) {
. . .
        case Grid_Delete: {
            // Here, we will perform the insert action by using DataManager.
            gData.remove('OrderID', { OrderID: action.payload.data[0]['OrderID'] });
            const updatedData = gData.executeLocal(new Query());
            initialState.data = [...updatedData];
            const count = updatedData.length;
            const gridData = new DataManager(updatedData).executeLocal(action.gQuery);
            // Execute the grid page query... based on your skip and take values.
            const currentPageData = new DataManager(gridData).executeLocal(new Query().skip(action.payload.state.skip).take(action.payload.state.take));
            // We need to return the grid data as result and count with object type.
            return ({
                data: { result: currentPageData, count: filter.length ? gridData.length : count }, isUpdated: true,
            })
        }
        . . .
}
```

Refer to the following screenshot for deleting requests.

![Delete request](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Delete-request.jpg)Finally, call the **_endEdit()_** to complete the **_dataSourceChanged_** event process in the **_componentDidUpdate_** lifecycle method of React.

```js
componentDidUpdate(prevProps, prevState, snapshot) {
    if (this.props.data1.isUpdated) {
        this.gState.endEdit(); // To complete the grid CRUD - async process. 
        
    }
}
```

Now, we have successfully removed and updated the pager item.

![Deleting data in React DataGrid](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Deleting-data-in-React-DataGrid.jpg)

### Run the application

Finally, run the application.

| `npm start` |

### GitHub reference

Refer to the complete example for [Syncfusion React Data Grid and performing CRUD actions using Redux without middleware](https://github.com/SyncfusionExamples/ej2-react-grid-redux "Link to React Data Grid and performing CRUD actions using Redux without middleware demo on GitHub").

## Update the application state using Redux with middleware

Redux [middleware](https://redux.js.org/tutorials/fundamentals/part-4-store#middleware "Link to Middleware") acts as a medium between dispatching an action and handing over the action to the reducer. It is mainly used to handle asynchronous actions in your application. Refer to the following flow chart about Redux middleware.

![Flowchart for updating the application state using Redux with Middleware](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Flowchart-for-updating-the-application-state-using-Redux-with-Middleware-1.png)

### Create JSON server

Install the JSON server in your application to create a REST JSON API web service.

| `npm install -g json-server` |

### Add JSON server to your application

Follow these steps to add JSON server to your application:

1. First, create a new a folder in your application. Here, we have created a folder named DataAPI.  
 ![Create a folder named DataAPI](https://www.syncfusion.com/blogs/wp-content/uploads/2021/09/Create-a-folder-named-DataAPI.jpg)
2. Then, open the DataAPI folder and add the package.json file by using the following command.  

| `npm init --yes` |

3. Add the JSON server to your application using the following command.  

| `npm install --save json-server` |

4. Now, create a data.json file in your application.
```js
"orderdetails": [
        {
            "OrderID": 10248,
            "CustomerID": "VINET",
            "OrderDate": "1996-07-04T00:00:00.000Z",
            "ShippedDate": "1996-07-16T00:00:00.000Z",
            "Freight": 32.38,
            "ShipName": "Vins et alcools Chevalier",
            "ShipAddress": "59 rue de l\"Abbaye",
            "ShipCity": "Reims",
            "ShipRegion": null,
            "ShipCountry": "France"
        },
        {
            "OrderID": 10249,
            "CustomerID": "TOMSP",
            "OrderDate": "1996-07-05T00:00:00.000Z",
            "ShippedDate": "1996-07-10T00:00:00.000Z",
            "Freight": 11.61,
            "ShipName": "Toms Spezialitäten",
            "ShipAddress": "Luisenstr. 48",
            "ShipCity": "Münster",
            "ShipRegion": null,
            "ShipCountry": "Germany"
        },
. . .
]
```
5. Then, set the portal number in the **package.json** file.
```js
{
    "name": "DataAPI",
        "version": "1.0.0",
            "description": "",
                "main": "index.js",
                    "scripts": {
        "start": "json-server -p 3007 -w data.json"
    },
    "keywords": [],
        "author": "",
            "license": "ISC",
                "dependencies": {
        "json-server": "^0.16.3"
    }
}
```

### Run the application

Now, run the application using the following command.

| `json - server--watch db.json--port 3007` |

If you have already configured and added the **run** command in the package.json, then run the app with this command.

| `npm start` |

### Add Thunk middleware to your application

[Redux-Thunk](https://www.npmjs.com/package/redux-thunk "Link to redux-thunk npm package") is a middleware that allows us to perform any asynchronous action. For more details, refer to the [Redux middleware](https://redux.js.org/api/applymiddleware "Link to applyMiddleware documentation") documentation.

- Use the following command to add the **Thunk middleware** to your application.  

| `npm install --save redux-thunk` |

- Then, import the **_Thunk middle-ware_** module from the **_redux-thunk_** and use it in your application.

#### Axios

- Now, install the [axios](https://www.npmjs.com/package/axios "Link to axios npm package") in your application to send the HTTP request through it.

| `npm install --save axios` |

#### Store

We explained the store action in the [Integrating Redux service in your application](#store) section. Now, define your action in the **store.js** file.

**[store.js]**

```js
import thunk from 'redux-thunk';

import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from 'redux-thunk';
import reducer from "../reducer/reducer";// Import your reducer.
const initialState = {};

const middleware = [thunk];
const store = new createStore(reducer, initialState, applyMiddleware(...middleware));//Give the reducer name.
export default store;
```

Now, let’s add the DataService file under the service folder.

**[serverapi.js]**

```js
import axios from 'axios';
export default axios.create({
    baseURL: "http://localhost:3007",
    headers: {
        "Content-type": "application/json"
      }
})
```

**[order.js]**

```js
import http from "../serverapi";

class orderDataService {
    getAll(gState) {
        debugger
        let cpage = (gState.skip / gState.take) + 1;
        let sortColumnName = ''
        let sortDirection = ''

        let params = {
            _page: cpage,
            _limit: gState.take
        }
        // Push the sorted columns.
        if (gState.sorted) {
            for (let i = gState.sorted.length; i > 0; i--) {
                let sortD = gState.sorted[i - 1].direction == "ascending" ? 'asc' : 'desc';
                let sortCol = gState.sorted[i - 1].name;
                sortColumnName = sortColumnName == '' ? sortCol : sortColumnName + ',' + sortCol;
                sortDirection = sortDirection == '' ? sortD : sortDirection + ',' + sortD;
            }
            params._sort = sortColumnName;
            params._order = sortDirection;
        }

        if (gState.where) {
            let filterCol = gState.where[0].predicates;
            for (let i = 0; i < filterCol.length; i++) {
                let optr = filterCol[i].operator == 'contains' ? '_like' : filterCol[i].operator == 'greaterthan' ? '_gte' : filterCol[i].operator == 'lessthan' ? '_lte' : '';
                params[filterCol[i].field + optr] = filterCol[i].value;
            }
        }
        return http.get("/orderdetails",
            {
                params
            });
    }

    create(data) {
        return http.post("/orderdetails", data);
    }

    update(id, data) {
        return http.put(`/orderdetails/${id}`, data);
    }

    delete(id) {
        return http.delete(`/orderdetails/${id}`);
    }

    deleteAll() {
        return http.delete(`/orderdetails`);
    }

    findByTitle(title) {
        return http.get(`/orderdetails?title=${title}`);
    }
}

export default new orderDataService();
```

#### Action

We explained the action types in the [Integrating Redux service in your application](#action-and-its-types) section. Now, define your action in the **action.js** file.

```js
import orderDataService from "../services/order"
export const Grid_Paging = "Grid_Paging";
export const Grid_Sorting = "Grid_Sorting";
export const Grid_Filtering = "Grid_Filtering";
export const Grid_Add = "Grid_Add";
export const Grid_Editing = "Grid_Editing";
export const Grid_Delete = "Grid_Delete";

export const getData = (gquery, gState) => async (dispatch) => {
    try {
        if (gState.action && gState.action == 'edit') {
            const updatedData = await orderDataService.update(gState.primaryKeyValue[0], gState.data)
            dispatch({
                type: Grid_Editing,
                payload: updatedData.data,
                gQuery: gquery,
                gState: gState,
                tCount: parseInt(updatedData.headers["x-total-count"]),
            })
        }
        if (gState.action && gState.action == 'add') {
            const addedData = await orderDataService.create(gState.data)
            dispatch({
                type: Grid_Add,
                payload: addedData.data,
                gQuery: gquery,
                gState: gState,
                tCount: parseInt(addedData.headers["x-total-count"]),
            })
        }
        if (gState.requestType && gState.requestType == 'delete') {
            const deletedData = await orderDataService.remove(gState.data[0]['id'])
            dispatch({
                type: Grid_Delete,
                payload: deletedData.data,
                gQuery: gquery,
                gState: gState,
                tCount: parseInt(deletedData.headers["x-total-count"]),
            })
        }
        else {
            const res = await orderDataService.getAll(gState);
            dispatch({
                type: Grid_Paging,
                payload: res.data,
                gQuery: gquery,
                gState: gState,
                tCount: parseInt(res.headers["x-total-count"]),
            })
        }
    } catch (error) {

    }
```

**Note:** Also, refer to the [JSON server data operations demo on GitHub](https://github.com/typicode/json-server "Link to JSON server data operations demo on GitHub") for more details.

#### Reducer

We explained the reducer in the [Integrating Redux Service in your application](#reducer) section. Now, define your action in the **reducer.js** file like in the following code example.

```js
import { orderDetails } from '../data';
import { DataManager, Query } from '@syncfusion/ej2-data';
import {
    Grid_Paging, Grid_Sorting, Grid_Filtering,
    Grid_Add, Grid_Editing, Grid_Delete
} from './action';

// Initially the Grid dataStateChange event is not triggered. Here, we set the default pageSize as take.. 
//you can set the pageSize based on your application. 
const initialPage = { skip: 0, take: 12 }
const initialState = {
    data: [],
    error: false,
    result: [],
    count: 0,
    isUpdated: false
}

const reducer = (state = initialState, action, gquery) => {

    switch (action.type) {
        case Grid_Paging: {
            return ({
                data: { result: action.payload, count: action.tCount }, isUpdated: false
            })
        }
        case Grid_Filtering: {
            return ({
                data: { result: action.payload, count: action.tCount }, isUpdated: false
            })
        }
        case Grid_Sorting: {
            return ({
                data: { result: action.payload, count: action.tCount }, isUpdated: false
            })
        }
        case Grid_Add: {
            return ({
                data: { result: action.payload, count: action.tCount }, isUpdated: true,
            })
        }
        case Grid_Editing: {
            return ({
                data: { result: action.payload, count: action.tCount }, isUpdated: true,
            })
        }
        case Grid_Delete: {
            return ({
                data: { result: action.payload, count: action.tCount }, isUpdated: true,
            })
        }
        default: {
            return ({ data: { result: [], count: 0 } })
        }
    }
}
export default reducer;
```

**Note:** You need to return the **_result_** and **_count_** format in your application and configure the [Provider](#provider), [Connect](#connect), [CreateSelector](#createselector).

#### Adding Data Grid to your application

We explained how to add the [Data Grid component](#create-a-react-app-and-integrate-syncfusion-react-datagrid) in your application and [handle the Data Grid actions](#fetch-data). Please see the following Data Grid code example.

**[App.js]**

```js
class App extends React.Component {
    constructor() {
        super();
        this.gState = {};
    }
    pageSettings = { pageSize: 6 };
    validationRule = { required: true };
    orderidRules = { required: true, number: true };
    editOptions = { allowEditing: true, allowAdding: true, allowDeleting: true };
    toolbarOptions = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
    filterOptions = { operator: "contains" }

    componentDidMount() {
        const query = this.gridInstance.getDataModule().generateQuery()
        this.props.getData(query, { skip: 0, take: 12 })
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        this.gridInstance.hideSpinner();
        if (this.props.data1.isUpdated) {
            this.gState.endEdit(); // To complete grid CRUD - async process 
            // this.gridInstance.freezeRefresh();
        }
    }

    render() {
        // Render the EJ2 Grid component. 
        return <GridComponent ref={grid => this.gridInstance = grid} dataSource={this.props.data1.currentData} dataStateChange={this.dataStateChange.bind(this)} dataSourceChanged={this.dataSourceChanged.bind(this)} allowSorting={true} editSettings={this.editOptions} toolbar={this.toolbarOptions} allowFiltering={true} allowPaging={true}>
            <ColumnsDirective>
                <ColumnDirective field='id' headerText='Order ID' width='140' textAlign='Right' validationRules={this.orderidRules} isPrimaryKey={true}></ColumnDirective>
                <ColumnDirective field='CustomerID' headerText='Customer Name' filter={this.filterOptions} width='150' validationRules={this.validationRule}></ColumnDirective>
                <ColumnDirective field='Freight' headerText='Freight' width='140' format='C2' textAlign='Right' editType='numericedit' ></ColumnDirective>
                <ColumnDirective field='ShipCountry' headerText='Ship Country' filter={this.filterOptions} width='150' ></ColumnDirective>
            </ColumnsDirective>
            <Inject services={[Page, Sort, Filter, Edit, Toolbar]} />
        </GridComponent>
    }
};
const mapStateToProps = (state, props) => {
    // UpdateData is a reselect selector.
    return { data1: UpdateData(state) }
}

// const mapDispatchToProps = dispatch => {
// return {
// dispatch
// }
// }

export default connect(
    mapStateToProps, { getData }

)(App);
```

Now, define the [_dataStateChange_](https://ej2.syncfusion.com/react/documentation/api/grid/#datastatechange "Link to dataStateChange property of React DataGrid") and [_dataSourceChange_](https://ej2.syncfusion.com/react/documentation/api/grid/#datasourcechanged "Link to dataSourceChanged property of React DataGrid") events in the App.js file.

```js
    dataStateChange(args) {
        // This event will be triggered when performing any grid action.

        const query = this.gridInstance.getDataModule().generateQuery(true); // get the Grid query without pagination

        // Dispatch the page query.
        if (args.action.requestType == "paging" || args.action.requestType == "refresh" || args.action.requestType == "delete") {
            this.props.getData(query, args);
        }
        // Dispatch the filtering query.
        if (args.action.requestType == 'filtering') {
            this.props.getData(query, args);
        }
        // Dispatch the sorting query.
        if (args.action.requestType == 'sorting') {
            this.props.getData(query, args);
        }
    }
    dataSourceChanged(state) {
        // This event will be triggered when we perform any CRUD action.

        this.gState = Object.assign(this.gState, state); // Store the grid async process.

        const query = this.gridInstance.getDataModule().generateQuery(true); // Get the Grid query without pagination.

        // Dispatch the editing action.
        if (state.action == 'edit') {
            this.props.getData(query, state);
        }

        // Dispatch the insert action.
        else if (state.action == 'add') {
            this.props.getData(query, state);
        }

        // Dispatch the delete action.
        else if (state.requestType == 'delete') {
            this.props.getData(query, state);
        }
        else {
            this.props.getData(query, state);

        }
    }
```

#### Run the application

Finally, run the application using the following command.

| `npm start` |

#### GitHub reference

You can get the complete working example for the [Syncfusion React Data Grid and preforming CRUD action using Redux](https://github.com/SyncfusionExamples/ej2-react-grid-redux-thunk "Link to React Data Grid and preforming CRUD action using Redux with Thunk middleware demo on GitHub").

## Conclusion

Thanks for reading! In this blog post, we have seen how to perform CRUD actions in the Syncfusion [React Data Grid](https://www.syncfusion.com/react-ui-components/react-data-grid "Link to React Data Grid") using the Redux service in your application. Also, peruse our [React Data Grid demos](https://ej2.syncfusion.com/react/demos/#/material/grid/overview "Link to React Data Grid online demos") and [documentation](https://ej2.syncfusion.com/react/documentation/grid/data-binding/ "Link to Data Binding in React Grid component") for more details. Try out this complete guide and leave your feedback in the comments section below.

Syncfusion Data Grid is also available in the [Blazor](https://www.syncfusion.com/blazor-components/blazor-datagrid "Link to Blazor DataGrid"), ASP.NET ([Core](https://www.syncfusion.com/aspnet-core-ui-controls/grid "Link to ASP.NET Core DataGrid"), [MVC](https://www.syncfusion.com/aspnet-mvc-ui-controls/grid "Link to ASP.NET MVC DataGrid"), [WebForms](https://www.syncfusion.com/jquery/aspnet-web-forms-ui-controls/datagrid "Link to ASP.NET WebForms DataGrid")), [JavaScript](https://www.syncfusion.com/javascript-ui-controls/js-data-grid "Link to JavaScript DataGrid"), [Angular](https://www.syncfusion.com/angular-ui-components/angular-grid "Link to Angular DataGrid"), [React](https://www.syncfusion.com/react-ui-components/react-data-grid "Link to React Data Grid"), [Vue](https://www.syncfusion.com/vue-ui-components/vue-grid "Link to Vue DataGrid"), [Xamarin](https://www.syncfusion.com/xamarin-ui-controls/xamarin-datagrid "Link to Xamarin DataGrid"), [Flutter](https://www.syncfusion.com/flutter-widgets/flutter-datagrid "Link to Flutter DataGrid"), [UWP](https://www.syncfusion.com/uwp-ui-controls/datagrid "Link to UWP DataGrid"), [WinForms](https://www.syncfusion.com/winforms-ui-controls/datagrid "Link to WinForms DataGrid"), [WPF](https://www.syncfusion.com/wpf-ui-controls/datagrid "Link to WPF DataGrid"), and [WinUI](https://www.syncfusion.com/winui-controls/datagrid "Link to WinUI DataGrid") platforms.

For existing customers, the newest version is available for download from the [License and Downloads](https://www.syncfusion.com/account/downloads "Link to license and download page of Essential Studio") page. If you are not yet a Syncfusion customer, you can try our 30-day [free trial](https://www.syncfusion.com/downloads "Link to the free evaluation of the Syncfusion Essential Studio") to check out the available features. Also, check out our samples on [GitHub](https://github.com/syncfusion "Link to the Syncfusion examples on GitHub").

You can contact us through our [support forums](https://www.syncfusion.com/forums "Link to the Syncfusion support forum"), [Direct-Trac](https://www.syncfusion.com/support/directtrac/incidents "Link to the Syncfusion support system Direct Trac"), or [feedback portal](https://www.syncfusion.com/feedback/react "Link to Syncfusion Feedback Portal"). We are always happy to assist you!

## Related blogs

- [How to Develop a Flight Tracker Application with React Gantt Chart](https://dev.to/syncfusion/how-to-develop-a-flight-tracker-application-with-react-gantt-chart-1p5h "Link to How to Develop a Flight Tracker Application with React Gantt Chart blog")
- [Top 5 Features of React Data Grid](https://dev.to/sureshmohan/top-5-features-of-react-data-grid-2ko8-temp-slug-2777752 "Link to Top 5 Features of React Data Grid blog")
- [How to Integrate Syncfusion React Components into a Meteor Application](https://www.syncfusion.com/blogs/post/how-to-integrate-syncfusion-react-components-into-a-meteor-application.aspx "Link to How to Integrate Syncfusion React Components into a Meteor Application blog")
- [Unit Testing in React with Jest and Enzyme Frameworks](https://dev.to/syncfusion/unit-testing-in-react-with-jest-and-enzyme-frameworks-1g97 "Link to Unit Testing in React with Jest and Enzyme Frameworks blog")