React Grid: Cell Renderer
The job of the grid is to lay out the cells. By default the grid will create the cell values using simple text. If you want more complex HTML inside the cells you can achieve this using cell renderers.
Simple Cell Renderer
Below is a simple example of cell renderer as a Hook:
export default props => {
const cellValue = props.valueFormatted ? props.valueFormatted : props.value;
const buttonClicked = () => {
alert(`${cellValue} medals won!`)
}
return (
<span>
<span>{cellValue}</span>
<button onClick={() => buttonClicked()}>Push For Total</button>
</span>
);
}
And here is the same example as a Class-based Component:
export default class TotalValueRenderer extends Component {
constructor(props) {
super(props);
this.state = {
cellValue: TotalValueRenderer.getValueToDisplay(props)
}
}
// update cellValue when the cell's props are updated
static getDerivedStateFromProps(nextProps) {
return {
cellValue: TotalValueRenderer.getValueToDisplay(nextProps)
};
}
buttonClicked() {
alert(`${this.state.cellValue} medals won!`)
}
render() {
return (
<span>
<span>{this.state.cellValue}</span>
<button onClick={() => this.buttonClicked()}>Push For Total</button>
</span>
);
}
static getValueToDisplay(params) {
return params.valueFormatted ? params.valueFormatted : params.value;
}
};
Simple Cell Renderer Example
The example below shows a simple cell renderer in action. It uses a cell renderer to render a hash (#
) symbol for each medal won
(instead of the medal count), and the MedalCellRenderer
defined in the code snippet above for the Total
column:
Cell Renderer Component
When a React component is instantiated the grid will make the grid APIs, a number of utility methods as well as the cell &
row values available to you via props
.
The interface for values available on both the initial props
value, as well as on futures props
updates or subsequent refresh
calls
(see below for details on refresh
) are as follows:
interface ICellRendererParams {
value: any, // value to be rendered
valueFormatted: any, // value to be rendered formatted
getValue: () => any, // convenience function to get most recent up to date value
setValue: (value: any) => void, // convenience to set the value
formatValue: (value: any) => any, // convenience to format a value using the column's formatter
data: any, // the row's data
node: RowNode, // row node
colDef: ColDef, // the cell's column definition
column: Column, // the cell's column
rowIndex: number, // the current index of the row (this changes after filter and sort)
api: GridApi, // the grid API
eGridCell: HTMLElement, // the grid's cell, a DOM div element
eParentOfValue: HTMLElement, // the parent DOM item for the cell renderer, same as eGridCell unless using checkbox selection
columnApi: ColumnApi, // grid column API
context: any, // the grid's context
refreshCell: () => void // convenience function to refresh the cell
registerRowDragger: (rowDraggerElement: HTMLElement) => void; // convenience function to register a DOM element as Row Dragger
}
Registering Cell Renderers with Columns
See the section registering custom components for details on registering and using custom cell renderers.
Component Refresh
Component refresh needs a bit more explanation. Here we go through some of the finer details.
Events Causing Refresh
When the grid can refresh a cell (instead of replacing it altogether) then the update can occur in one of two ways:
- A component will have it's props refreshed, with all corresponding lifecycle methods called (i.e.
componentWillReceiveProps
,getDerivedStateFromProps
) - A
refresh
method that you implement will be called
If you provide a refresh
method then this method will be called over the component's props being refreshed.
Note that if you're using Hooks for a Cell Renderer and decide to implement the refresh
method then you'll need to expose it with
forwardRef
& useImperativeHandle
. Please refer to the Hook documentation (or the examples on this page) for more information.
In the context of "refresh" being referenced from here on then it'll refer to either of the mechanisms above, whichever you choose to implement.
The grid can refresh the data in the browser, but not every refresh / redraw of the grid results in the refresh method of your cell renderer getting called, or for props to be updated.
The following items are those that do cause refresh to be called:
- Calling
rowNode.setDataValue(colKey, value)
to set a value directly onto therowNode
. This is the preferred API way to change one value from outside of the grid. - When editing a cell and editing is stopped, so that cell displays new value after editing.
- Calling
api.refreshCells()
to inform grid data has changed (see Refresh). - Bound
rowData
changes andimmutableData
is set (see Immutable Data).
If any of the above occur and the grid confirms the data has changed via Change Detection, then the refresh()
method will be called.
The following will not result in the cell renderer's refresh method being called:
- Calling
rowNode.setData(data)
to set new data into arowNode
. When you set the data for the whole row, the whole row in the DOM is recreated again from scratch. - Scrolling the grid vertically causes columns (and their containing cells) to be removed and inserted due to column virtualisation.
All of the above will result in the component being destroyed and recreated.
If the grid cannot safely determine a given row then cell components will always be replaced, instead of refreshed - as such we strongly encourage Immutable Data be used whenever possible.
Doing so will allow AG Grid to intelligently determine which cells have changed in a given update and only refresh those cells.
Using Immutable Data is analogous to providing a key
to an array of components in React - it allows for cells to be refreshed (if possible) instead
of being replaced.
Grid vs Component Refresh
If you choose to implement the refresh
method, then note that this method returns back a boolean value. If you do not
want to handle the refresh in the cell renderer, just return back false
from an otherwise empty method. This will
indicate to the grid that you did not refresh and the grid will instead destroy the component and create another instance of your component from scratch instead.
Change Detection
As mentioned in the section on Change Detection, the refresh of the cell will not take place if the value getting rendered has not changed.
Cell Renderer Component Lifecycle
The lifecycle of the cell renderer is as follows:
- The component will be instantiated.
- The component's GUI will be inserted into the grid 0 or 1 times (the component could get destroyed first, i.e. when scrolling quickly).
- The component's props are updated, or
refresh()
is called, 0...n times (i.e. it may never be called, or called multiple times). - The component is destroyed once.
In other words, component instantiation and destruction are always called exactly once. The component's GUI will
typically get rendered once unless the component is destroyed first. The component's props are updated/refresh()
is optionally called multiple times.
Cell Rendering Flow
The diagram below (which is taken from the section Value Getters & Formatters) summarises the steps the grid takes while working out what to render and how to render.
In short, a value is prepared. The value comes using either the colDef.field
or the colDef.valueGetter
. The value is also optionally passed through a colDef.valueFormatter
if it exists. Then the value is finally placed into the DOM, either directly, or by using the chosen colDef.cellRenderer
.
Complementing Cell Renderer Params
On top of the parameters provided by the grid, you can also provide your own parameters. This is useful if you want to 'configure' your cell renderer. For example, you might have a cell renderer for formatting currency but you need to provide what currency for your cell renderer to use.
Provide params to a cell renderer using the colDef option cellRendererParams
.
// define cellRenderer to be reused
const ColourCellRenderer = props => <span style={{color: props.color}}>{props.value}</span>;
const GridExample = () => {
// other properties & methods
frameworkComponents = {
'colourCellRenderer': ColourCellRenderer
};
return (
<div className="ag-theme-alpine">
<AgGridReact
frameworkComponents={frameworkComponents}
...other properties>
<AgGridColumn headerName="Colour 1" field="value" cellRenderer="colourCellRenderer" cellRendererParams={{ color: 'guinnessBlack' }} />
<AgGridColumn headerName="Colour 2" field="value" cellRenderer="colourCellRenderer" cellRendererParams={{ color: 'irishGreen' }} />
</AgGridReact>
</div>
);
};
Data in Cell Renderers
Sometimes the data
property in the parameters given to a cell renderer might not be populated. This can happen for
example when using row grouping (where the row node has aggData
and groupData
instead of data
), or when rows are
being loaded in the Infinite Row Model and do not yet have data. It is best to check that data
does exist before accessing it in your cell renderer, for example:
// define cellRenderer to be reused
const CellRenderer = props => <span>{props.data && props.data.theBoldValue}</span>;
Cell Renderer Function
Instead of using a React component, it's possible to use a simple function for a cell renderer.
This is probably most useful if you have a simple String value to render and want to avoid the overhead of an actual React component.
In the example below we're outputting a simple string value that depends on the cell value:
const GridExample = () => {
// other properties & methods
return (
<div className="ag-theme-alpine">
<AgGridReact
...other properties>
<AgGridColumn headerName="Value" field="value" cellRenderer={params => params.value > 1000 ? "LARGE VALUE" : "SMALL VALUE"} />
</AgGridReact>
</div>
);
};
It is also possible to write a JavaScript-based cell renderer function - refer to the docs here for more information
Complex Cell Renderer Example
The example below shows five columns formatted, demonstrating each of the methods above.
- 'Month' column uses
cellStyle
to format each cell in the column with the same style. - 'Max Temp' and 'Min Temp' columns uses the Function method to format each cell in the column with the same style.
- 'Days of Air Frost' column uses the Component method to format each cell in the column with the same style
- 'Days Sunshine' and 'Rainfall (10mm)' use simple functions to display icons.
Accessing Cell Renderer Instances
After the grid has created an instance of a cell renderer for a cell it is possible to access that instance. This is useful if you want to call a method that you provide on the cell renderer that has nothing to do with the operation of the grid. Accessing cell renderers is done using the grid API getCellRendererInstances(params)
.
// function takes params to identify which cells and returns back a list of cell renderers
function getCellRendererInstances(params: GetCellRendererInstancesParams): ICellRendererComp[];
// params object for the above
interface GetCellRendererInstancesParams {
// an optional list of row nodes
rowNodes?: RowNode[];
// an optional list of columns
columns?: (string | Column)[];
}
An example of getting the cell renderer for exactly one cell is as follows:
// example - get cell renderer for first row and column 'gold'
const firstRowNode = gridOptions.api.getDisplayedRowAtIndex(0);
const params = { columns: ['gold'], rowNodes: [firstRowNode] };
const instances = gridOptions.api.getCellRendererInstances(params);
if (instances.length > 0) {
// got it, user must be scrolled so that it exists
const instance = instances[0];
}
Not that this method will only return instances of the cell renderer that exists. Due to row and column virtualisation, renderers will only exists for the user can actually see due to horizontal and vertical scrolling.
The example below demonstrates custom methods on cell renderers called by the application. The following can be noted:
- The medal columns are all using the user defined
MedalCellRenderer
. The cell renderer has an arbitrary methodmedalUserFunction()
which prints some data to the console. - The Gold method executes a method on all instances of the cell renderer in the gold column.
- The First Row Gold method executes a method on the gold cell of the first row only. Note that the
getCellRendererInstances()
method will return nothing if the grid is scrolled past the first row. - The All Cells method executes a method on all instances of all cell renderers.
If your are using a framework component (detailed below), then the returned object is a wrapper and you can get the underlying cell renderer using getFrameworkComponentInstance()
// example - get cell renderer for first row and column 'gold'
const firstRowNode = gridOptions.api.getDisplayedRowAtIndex(0);
const params = { columns: ['gold'], rowNodes: [firstRowNode] };
const instances = gridOptions.api.getCellRendererInstances(params);
if (instances.length > 0) {
// got it, user must be scrolled so that it exists
const wrapperInstance = instances[0];
const frameworkInstance = wrapperInstance.getFrameworkComponentInstance();
}
Example: Rendering using more complex Components
This example illustrates a few different ideas:
- Custom Cell Renderers
- Parent/Child Communication using context
-
Storing the Grid API via the "Grid Ready" event, and using it later
Note that the hook version of this example makes use of useImperativeHandle
to expose methods to the grid (and other components). Please
refer to the hook specific documentation for more information.
- Cell Renderer
- Simple Cell Renderer
- Simple Cell Renderer Example
- Cell Renderer Component
- Cell Renderer Component
- Cell Renderer Component
- Cell Renderer Component
- Registering Cell Renderers with Columns
- Component Refresh
- Events Causing Refresh
- Grid vs Component Refresh
- Events Causing Refresh
- Grid vs Component Refresh
- Events Causing Refresh
- Grid vs Component Refresh
- Events Causing Refresh
- Grid vs Component Refresh
- Change Detection
- Cell Renderer Component Lifecycle
- Cell Renderer Component Lifecycle
- Cell Renderer Component Lifecycle
- Cell Renderer Component Lifecycle
- Cell Rendering Flow
- Complementing Cell Renderer Params
- Data in Cell Renderers
- Cell Renderer Function
- Cell Renderer Function
- Cell Renderer Function
- Cell Renderer Function
- Complex Cell Renderer Example
- Accessing Cell Renderer Instances
- Example: Rendering using more complex Components
- Example: Rendering using more complex Components
- Example: Rendering using more complex Components
- Example: Rendering using nested Modules