This page explains best practices for using React Hooks with AG Grid.
Row Data
When setting Row Data, we recommend using useState
to maintain a consistent array reference across renders.
const App = () => {
const [rowData, setRowData] = useState([
{make: "Toyota", model: "Celica", price: 35000},
{make: "Ford", model: "Mondeo", price: 32000},
{make: "Porsche", model: "Boxster", price: 72000}
]);
return <AgGridReact rowData={rowData} />;
};
If you do NOT use useState
and define the row data array within your component, then the grid will be provided with a new array each time the component is rendered. This will result in additional grid renders and may result in unexpected behaviour in the grid, such as row selection resetting.
For applications that do not update rowData then useMemo
is a valid alternative to useState
.
Immutable Data
If your application updates row data then it is strongly recommended to implement the getRowId
callback. The getRowId
callback returns a unique id for each row enabling the grid to maintain row state between updates. For example, if rows are selected and then the row data is updated, the grid is able to use the unique ids provided to maintain the current selection.
See Updating Row Data for more information and other benefits of providing the getRowId
callback.
Column Definitions
When setting Column Definitions, we recommend using useState
or useMemo
.
const App = () => {
const [columnDefs, setColumnDefs] = useState([
{field: 'make'},
{field: 'model'},
]);
return <AgGridReact columnDefs={columnDefs} />;
};
If you do NOT use useState
or useMemo
, then the grid will be provided with a new set of Column Definitions every time the component is rendered. This may result in unexpected behaviour in the grid, such as the column state (column order, width etc...) getting reset to match the column definitions provided.
If your application changes Column Definitions use useState
, otherwise use useMemo
.
Object Properties
For all properties that are Objects, e.g. defaultColDef
, sideBar
and statusBar
, we recommend useState
or useMemo
. If you do not use these hooks, then you risk resetting the grid's state each time a render occurs.
For example, when providing a defaultColDef
property do not define this inline or as a simple object on the component as this will result in a new instance on every render.
const App = () => {
// BAD - new instance on every render
const defaultColDef = { filter: true };
// BAD - new instance on every render
return <AgGridReact defaultColDef={{filter: true}} />;
};
Instead use useMemo
or useState
to ensure a consistent reference is maintained across renders.
const App = () => {
// GOOD - only one instance created
const defaultColDef = useMemo( ()=> { filter: true }, []);
return <AgGridReact defaultColDef={defaultColDef} />;
};
Simple Properties
Properties of simple types (string, boolean and number) do not need to use hooks as they are compared by value across renders.
const App = () => {
const rowBuffer = 0;
return (
<AgGridReact
// GOOD
rowBuffer={rowBuffer}
// GOOD
rowModelType='clientSide'
rowHeight={50}
/>
);
};
Callbacks
For Grid Options that accept functions, i.e isRowSelectable
we strongly recommend you use useCallback
to avoid resetting grid state on every render. For example, if you do not use a callback for isRowSelectable
then on every render the grid will receive a new function and have to re-run selection logic.
When using useCallback()
, make sure you set correct dependencies in order to avoid stale closures.
const App = () => {
const [count, setCount] = useState(0);
// BAD will re-run selection logic on every render
const isRowSelectable = (node) => node.data.value > count;
// GOOD will only re-run selection logic when count changes
const isRowSelectable = useCallback((node) => node.data.value > count, [count]);
return <AgGridReact isRowSelectable={isRowSelectable} />;
};
Event Listeners
For Event Listeners there is no requirement to use useCallback
as event handlers do not trigger updates within the grid. However, you may find it easier to be consistent with Callbacks and just always use useCallback
.
If you do use useCallback()
, make sure you set correct dependencies in order to avoid stale closures.
const App = () => {
const [clickedCount, setClickedCount] = useState(0);
// GOOD callback, no hook, no stale data
const onCellClicked = () => setClickedRow(clickedCount++);
// BAD callback - stale data, dependency missing, will ALWAYS print 0
const onCellValueChanged = useCallback( ()=> {
console.log(`number of clicks is ${clickedCount}`);
}, []);
// GOOD callback, no stale data
const onFilterOpened = useCallback( ()=> {
console.log(`number of clicks is ${clickedCount}`);
}, [clickedCount]);
return <AgGridReact
onCellClicked={onCellClicked}
onCellValueChanged={onCellValueChanged}
onFilterOpened={onFilterOpened}
/>;
};
Components
Custom Components can be referenced by Name or Direct Reference, see Registering Components. When providing a Direct Reference to the component AG Grid will avoid most unnecessary renders. However, if your component is rendered more than you expect, it may help to wrap it with memo
.
const MyCellRenderer = p => <span>{p.value}</span>;
const App = () => {
const [columnDefs] = useState([
// reference the Cell Renderer above
{ field: 'make', cellRenderer: MyCellRenderer },
// or put inline
{ field: 'model', cellRenderer: p => <span>{p.value}</span> },
// optionally for best performance, memo() the renderer,
{ field: 'price', cellRenderer: memo(MyCellRenderer) }
]);
return <AgGridReact columnDefs={columnDefs} />;
};
Debug Mode
A good way to diagnose if you are causing the grid to update on each render is to enable the debug
flag. In debug mode the grid will log extra details to the console including when a property has changed.
const App = () => {
return <AgGridReact debug />;
};
For example if you have defined defaultColDef
inline on your component then on every render you will see the following in the console with debug
enabled.
const App = () => {
return <AgGridReact defaultColDef={{ filter: true}} debug />;
};
AG Grid: Updated property defaultColDef from {filter: true} to {filter: true}
If there is no real difference between the old and new value then you should consider using useState
, useMemo
or useCallback
as detailed above.