Customising React Data Grid with hooks and functions
Tags: react, tutorial, aggrid
Published: 2021-09-28 09:45:23

---
title: Customising React Data Grid with hooks and functions
published: true
date: 2021-08-17 09:28:25 UTC
tags: React,Tutorial, aggrid
canonical_url: https://blog.ag-grid.com/customising-react-data-grid-with-hooks-and-functions/
---

 ![Customising React Data Grid with hooks and functions](https://blog.ag-grid.com/content/images/2021/08/customising-with-react-hooks-functions.png)

AG Grid allows extensive customisation by creating custom components. In this post we will create custom components for a React Data Grid to render cells, filter column values, and edit cell values.

An earlier version of this [post covers the same content with examples using classes](https://blog.ag-grid.com/learn-to-customize-react-grid-in-less-than-10-minutes/). In this post all code examples will be written using functions and React hooks.

- [Video](#video)
- [Custom Components](#custom-components)
- [Code and Getting Started](#code-and-getting-started)
- [Start Point](#start-point)
- [Cell Renderer](#cell-renderer)
- [Creating a Cell Editor](#creating-a-cell-editor)
- [Custom Filter](#custom-filter)
- [Next Steps](#next-steps)


## Video

{% youtube KT7kJ1V6stI %}

## Custom Components

We will build:

- A Cell Render which displays a number as a currency complete with currency symbol e.g. `35000` would become `€35,000.00`.
- A custom editor which restricts input keys to numbers only.
- A filter which uses a syntax to select a range of values ie. `30000 - 36000`.

## Code and Getting Started

The code for this example is in the [React-Data-Grid](https://github.com/ag-grid/react-data-grid) repo on Github in the [customization-demo-hooks](https://github.com/ag-grid/react-data-grid/tree/main/customization-demo-hooks) folder.

If you want to get started with AG Grid using React then a good place to start is the [Getting Started in 5 Minutes with React Data Grid and Hooks](https://blog.ag-grid.com/getting-started-with-react-hooks-and-ag-grid-in-5-minutes/) blog post, followed by our documentation [Getting Started Guide](https://ag-grid.com/react-grid/getting-started/).

## Start Point

The initial starting point was as follows in `CustomizedGrid.js`:

```
export function CustomGrid() {

    const [rowData, setRowData] = useState([]);

    const [colDefs, setColDefs] = useState(
        [
            {field:"make"},
            {field:"model"},
            {field:"price", editable:true}
        ]);

    useEffect(() => {
        fetch('https://www.ag-grid.com/example-assets/row-data.json')
            .then(result => result.json())
            .then(rowData => setRowData(rowData))
    }, []);

    return (
        <div className="ag-theme-alpine"
            style={{height: '400px', width: '600px'}}
        >
            <AgGridReact
                defaultColDef={{sortable: true, filter: true }}
                pagination={true}
                rowData={rowData}
                columnDefs={colDefs}>
            </AgGridReact>
        </div>
    );
}

```

This renders a grid and loads the data in from a server.

The CustomGrid component is used in the `App.js` directly:

```
import logo from './logo.svg';
import './App.css';
import {CustomGrid} from './CustomizedGrid.js'

function App() {
  return (
    <CustomGrid />
  );
}

export default App;

```

## Cell Renderer

The simplest component to add is a cell renderer from `NumberFormatter.js`:

```
import React, { Component } from 'react';

export function NumberFormatter(props){

        const value = Number(props.value);
        const text = value.toLocaleString(undefined, {style: 'currency', currency: 'EUR'});

        return (
            <span>{text}</span>
        );    
}

```

This is a function that has props, and these are detailed in the official documentation on the [Component Cell Renderer](https://www.ag-grid.com/react-grid/component-cell-renderer/) page.

The 'value' is the current value to be rendered in the cell.

All we do in this component is convert the `value` from `props` into a number and then a currency, which we return in a `span`.

This will render the value in the React Data Grid as follows:

![Customising React Data Grid with hooks and functions](https://blog.ag-grid.com/content/images/2021/08/custom-cell-renderer-in-action.png)

### Using the Cell Renderer

To use the Cell Renderer in the Grid component we:

- import it,
- add it to the column definition,
- define it in the grid's `frameworkComponents`.

Import it as normal:

```
import { NumberFormatter } from './NumberFormatter.js';

```

Add it to the column definition object, in this case I'm adding it to the `price` column.

```
            {field:"price", editable:true,
            cellRenderer: "numberFormatter",
            }

```

Then we add a new attribute into the grid definition JSX:

```
            frameworkComponents={{
                numberFormatter: NumberFormatter,
            }} 

```

The grid should then render the price in euros with a currency symbol.

## Creating a Cell Editor

The next component to look at is the Custom Cell Editor.

Cell editors are documented in the official documentation [React Data Grid Cell Editors](https://www.ag-grid.com/react-grid/component-cell-editor/).

This cell editor is a simple `input` field which restricts keypresses to numbers only. The value will be stored in state and stored in the grid as the edited value changes.

The basic structure of the cell editor is as follows:

```
import React, {forwardRef, useState, useRef, useEffect, useCallback, useImperativeHandle} from 'react';

export const NumericCellEditor = forwardRef((props, ref) => {

    const [value, setValue] = useState(parseInt(props.value));
    const refInput = useRef(null);

    return (
        <input 
            onKeyPress={onKeyPressListener}
            value={value}
            onChange={onChangeListener}
            ref={refInput} 
        />
    );  
});

```

The code above receives the `ICellEditorParams` as `props` defined in the [documentation](https://www.ag-grid.com/react-grid/component-cell-editor/), and a ref to the element.

Initially set the value state from the props.

To the above code I have to add the `onPressKeyListener` the `onChangeListener` and a hook for `useImperativeHandle` for the grid to use and handle the focus.

We need to set the focus on the input, in a class this would be done `afterGuiAttached` but we will use the `useEffect` hook:

```
    // afterGuiAttached
    useEffect( ()=> refInput.current.focus(), []);

```

The `onChangeListener` will amend the value as state using a callback to optimise for rendering.

```
    const onChangeListener = useCallback( event => setValue(event.target.value), []);

```

The `onKeyPressListener`, again uses the `useCallback` hook. It only allows numbers to be entered, so if a non numeric key is pressed it uses `event.preventDefault()` to ignore the keypress.

```
    const onKeyPressListener = useCallback( event => {
        if (!isNumeric(event.nativeEvent)) {
            event.preventDefault();
        }

        function isNumeric(event) {
            return /\d/.test(event.key);
        }
    }, []);

```

The final chunk of code is to allow the grid to receive the value, which requires use of the `useImperativeHandle` hook.

```
    useImperativeHandle(ref, () => {
        return {
            // the final value to send to the grid, on completion of editing
            getValue() {
                return value;
            }
        };
    });

```

Giving us a final set of code of:

```
import React, {forwardRef, useState, useRef, useEffect, useCallback, useImperativeHandle} from 'react';

export const NumericCellEditor = forwardRef((props, ref) => {

    const [value, setValue] = useState(parseInt(props.value));
    const refInput = useRef(null);

    useImperativeHandle(ref, () => {
        return {
            getValue() {
                return value;
            }
        };
    });

    const onKeyPressListener = useCallback( event => {
        if (!isNumeric(event.nativeEvent)) {
            event.preventDefault();
        }

        function isNumeric(event) {
            return /\d/.test(event.key);
        }
    }, []);

    const onChangeListener = useCallback( event => setValue(event.target.value), []);

    useEffect( ()=> refInput.current.focus(), []);

    return (
        <input 
            onKeyPress={onKeyPressListener}
            value={value}
            onChange={onChangeListener}
            ref={refInput} 
        />
    );  
});

```

This will render as a text input field:

![Customising React Data Grid with hooks and functions](https://blog.ag-grid.com/content/images/2021/08/cell-editor-using-hooks.png)

### Using The Editor in the Grid

To use the editor we: import, add to column def, and define in the framework components.

```
import {NumericCellEditor} from './NumericCellEditor.js';

```

Add to the column definition:

```
    {field:"price",editable:true,
    cellRenderer: "numberFormatter",
    cellEditor: "numericCellEditor",
    }

```

Expand the framework components definition:

```
    frameworkComponents={{
        numberFormatter: NumberFormatter,
        numericCellEditor: NumericCellEditor,
    }} 

```

Now when the editor is used the custom input element is used.

## Custom Filter

The custom filter will be an input that uses the format `low - high` e.g. `30000 - 35000` to show only values between 30000 euros and 35000 euros.

The custom filter will be created in `RangeFilter.js`.

Custom filters are documented in the official documentation for [React Data Grid Filter Component](https://www.ag-grid.com/react-grid/component-filter/)

The basic structure of a filter component is as follows:

```
import React, { useState, forwardRef, useRef, useImperativeHandle, setState, useCallback, useEffect } from 'react';

export const RangeFilter = forwardRef((props, ref) => {

    const [filter, setFilter] = useState('');
    const input = useRef(null);

    return (
        <form onSubmit={onSubmit}>
            <input name="filter" ref={input} defaultValue={filter}/>
            <button>Apply</button>
        </form>
    );

});

```

We will need to add a hook for `useImperativeHandle` to integrate with the grid, call the filter changed call back and handle the form we are using for input.

First set the focus to the input:

```
    useEffect( ()=> input.current.focus(), []);

```

And when the filter changes we want to call the grid callback defined in `props`:

```
    useEffect(() => {
        props.filterChangedCallback()
    }, [filter]);

```

Because we are using a form as the DOM component for input we need to handle the `onSubmit` event, but preventing the default form processing and using the state to set the filter value:

```
    const onSubmit = useCallback( event => {
        event.preventDefault();

        let currFilter = event.target.elements.filter.value;

        if (filter !== currFilter) {
            setFilter( currFilter);
        }
    });

```

Then we handle the grid's lifecycle call backs. The filter itself is implemented as `doesFilterPass`:

```
    useImperativeHandle(ref, () => {
        return {
            isFilterActive() {
                return filter !== '';
            },

            doesFilterPass(params) {
                const myfilter = filter.split('-');

                const gt = Number(myfilter[0]);
                const lt = Number(myfilter[1]);
                const value = params.node.data.price;
        
                return value >= gt && value <= lt;
            },
        
            getModel() {
                return {filter: filter};
            },

            setModel(model) {
                const filter = model ? model.filter : '';
                setState( filter);
            }
    
        };
    });

```

![Customising React Data Grid with hooks and functions](https://blog.ag-grid.com/content/images/2021/08/custom-filter.png)

### Using the Filter Component

Using the same pattern as before: import, add to column def, and define in the framework components.

```
import { RangeFilter } from './RangeFilter';

```

Add to the column definition:

```
    {field:"price",editable:true,
    cellRenderer: "numberFormatter",
    cellEditor: "numericCellEditor",
    filter: 'rangeFilter'
    }

```

Expand the framework components definition:

```
    frameworkComponents={{
        numberFormatter: NumberFormatter,
        numericCellEditor: NumericCellEditor,
        rangeFilter: RangeFilter
    }} 

```

Now when the editor is triggered the custom input element is used.

## Next Steps

The full code for this example is in the [react-data-grid][[https://github.com/ag-grid/react-data-grid](https://github.com/ag-grid/react-data-grid)] examples repo.

We also have some examples in the [React Data Grid Example][[https://blog.ag-grid.com/react-data-grid-example-projects/](https://blog.ag-grid.com/react-data-grid-example-projects/)] repo.

Or follow the [Getting Started](https://www.ag-grid.com/react-grid/getting-started/) guide in the documentation.