Dynamic Data Management: Refreshing and Updating Data in AG-Grid React
In the world of data grids, static information is rarely the norm. Applications constantly deal with evolving data—new records are added, existing ones are modified, and sometimes, the entire dataset needs to be reloaded. For developers working with AG-Grid in a React environment, mastering how to refresh and update data efficiently is crucial for building responsive and performant user interfaces.
This post, part of our AG-Grid React series, dives deep into the mechanisms AG-Grid provides for managing dynamic data. We'll explore various strategies, from simple rowData prop changes to advanced transaction updates, ensuring your grids stay up-to-date with minimal fuss.
Understanding Refreshing vs. Updating Data
Before we delve into the how-to, let's clarify the distinction between "refreshing" and "updating" data in the context of AG-Grid:
- Refreshing Data: This generally refers to re-fetching or entirely replacing the dataset that the grid is displaying. It's like pressing a "reload" button for all the data. This is common when the underlying source of data (e.g., an API) has changed significantly, or you want to ensure the grid reflects the latest state from the server.
- Updating Data: This involves making targeted changes to specific rows or cells within the existing dataset. This could mean adding a new row, modifying an existing one, or removing one. AG-Grid is highly optimized to handle these granular changes efficiently, often with animations and without re-rendering the entire grid.
Refreshing Data in AG-Grid React
The most common way to refresh your AG-Grid's data in React is by managing its rowData prop via React state.
1. Updating the rowData Prop
AG-Grid, being a React component, reacts to changes in its props. When the rowData prop is updated with a new array of data, AG-Grid will automatically detect this change and re-render the grid rows.
import React, { useState, useEffect, useCallback } from 'react';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
const MyGridComponent = () => {
const [rowData, setRowData] = useState([]);
const [columnDefs] = useState([
{ field: 'id' },
{ field: 'make' },
{ field: 'model' },
{ field: 'price' }
]);
const fetchData = useCallback(async () => {
// Simulate fetching data from an API
const response = await new Promise(resolve =>
setTimeout(() => {
const newData = [
{ id: 1, make: 'Toyota', model: 'Celica', price: 35000 + Math.floor(Math.random() * 1000) },
{ id: 2, make: 'Ford', model: 'Mondeo', price: 32000 + Math.floor(Math.random() * 1000) },
{ id: 3, make: 'Porsche', model: 'Boxster', price: 72000 + Math.floor(Math.random() * 1000) },
];
resolve(newData);
}, 500)
);
setRowData(response);
}, []);
useEffect(() => {
fetchData(); // Initial data load
}, [fetchData]);
const handleRefreshData = () => {
fetchData(); // Re-fetch and update rowData
};
return (
<div className="ag-theme-alpine" style={{ height: 400, width: 600 }}>
<button onClick={handleRefreshData}>Refresh All Data</button>
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
animateRows={true}
></AgGridReact>
</div>
);
};
export default MyGridComponent;
In this example, calling fetchData() updates the component's rowData state, which in turn causes AG-Grid to re-render with the new data. AG-Grid is smart enough to perform efficient diffing if your new rowData array shares many objects with the old one, especially if you provide a getRowId callback (more on this later).
2. Using gridApi.setRowData()
While updating the rowData prop is the idiomatic React way, you can also directly call gridApi.setRowData(newData). This is generally used in scenarios where you have direct access to the grid API (e.g., from an event handler or a ref) and need to bypass React's state update cycle for some reason, though it's less common than the prop-based approach for full data refreshes.
// ... (inside MyGridComponent) ...
const [gridApi, setGridApi] = useState(null);
const onGridReady = (params) => {
setGridApi(params.api);
fetchData(); // Initial data load
};
const handleRefreshDataViaApi = async () => {
if (gridApi) {
// Simulate fetching new data
const response = await new Promise(resolve =>
setTimeout(() => {
const newData = [ /* ... new data ... */ ];
resolve(newData);
}, 500)
);
gridApi.setRowData(response); // Update data directly via API
}
};
return (
<div className="ag-theme-alpine" style={{ height: 400, width: 600 }}>
<button onClick={handleRefreshDataViaApi}>Refresh Data (via API)</button>
<AgGridReact
onGridReady={onGridReady}
columnDefs={columnDefs}
// rowData is managed internally or by API for full refresh
animateRows={true}
></AgGridReact>
</div>
);
3. Refreshing Cell Renderers and Styles: gridApi.refreshCells()
Sometimes, the underlying data itself hasn't changed, but the way it's presented might need re-evaluation. This is where gridApi.refreshCells() comes in handy. It forces specific cells or all cells to re-render, useful for:
- When a cell renderer relies on external state that has changed.
- When cell styles (
cellClassRules) depend on a global theme or external condition that has updated. - After manually updating cell data without triggering a
rowDatachange (less common in React).
You can refresh all cells, specific cells by row/column, or based on a filter.
// To refresh all cells in the grid:
gridApi.refreshCells();
// To refresh cells for specific rows (e.g., row with ID 'xyz'):
gridApi.refreshCells({ rowNodes: [node1, node2] }); // rowNodes obtained from gridApi.getRowNode()
// To refresh cells for a specific column:
gridApi.refreshCells({ columns: ['make', 'price'] });
Updating Data in AG-Grid React (Specific Rows/Cells)
For granular updates—adding, editing, or deleting individual rows—AG-Grid offers powerful mechanisms that integrate well with React's philosophy of immutable data updates.
1. Immutable Updates via React State (Recommended for Simplicity)
The most React-friendly way to update specific rows is to create a new array for your rowData state, reflecting the changes. AG-Grid will then efficiently detect which rows have been added, updated, or removed, especially if you use getRowId.
Adding a new row:
const handleAddRow = () => {
const newId = rowData.length > 0 ? Math.max(...rowData.map(r => r.id)) + 1 : 1;
const newRow = { id: newId, make: 'Tesla', model: 'Model 3', price: 60000 };
setRowData(prevData => [...prevData, newRow]);
};
Updating an existing row:
const handleUpdateRow = (idToUpdate, newPrice) => {
setRowData(prevData =>
prevData.map(row =>
row.id === idToUpdate ? { ...row, price: newPrice } : row
)
);
};
Deleting a row:
const handleDeleteRow = (idToDelete) => {
setRowData(prevData =>
prevData.filter(row => row.id !== idToDelete)
);
};
This approach leverages React's state management and AG-Grid's efficient diffing algorithm. For this to work optimally, always provide a getRowId callback to AG-Grid. This function tells AG-Grid how to uniquely identify each row, enabling it to perform intelligent updates rather than re-rendering everything.
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
getRowId={params => params.data.id} // Essential for efficient immutable updates
animateRows={true}
></AgGridReact>
2. Transaction Updates with gridApi.applyTransaction()
For more complex, batched, or highly optimized updates, especially with animations, AG-Grid provides the gridApi.applyTransaction() method (or applyTransactionAsync() for asynchronous operations). This method allows you to specify a set of operations (add, remove, update) to be applied to the grid's data in a single transaction.
applyTransaction is powerful because it gives AG-Grid explicit instructions on what changed, allowing it to apply animations and perform very targeted DOM updates.
const handleTransactionUpdate = () => {
if (gridApi) {
const newRow = { id: 4, make: 'BMW', model: 'X5', price: 80000 };
const updatedRow = { id: 1, make: 'Toyota', model: 'Corolla', price: 28000 };
const rowToDelete = rowData.find(row => row.id === 2); // Assuming ID 2 exists
const transaction = {
add: [newRow],
update: [updatedRow],
remove: rowToDelete ? [rowToDelete] : [],
};
gridApi.applyTransaction(transaction);
// Important: If you're managing rowData via React state,
// you'll need to update your state to reflect this transaction
// if you want your component's state to be in sync with the grid.
// A common pattern is to let AG-Grid manage the data if using applyTransaction
// extensively, or re-fetching and setting rowData after transactions.
// For simple apps, `getRowData()` after transaction can give you the new state.
}
};
// ... In your return statement ...
<button onClick={handleTransactionUpdate}>Apply Transaction</button>
<AgGridReact
onGridReady={onGridReady}
columnDefs={columnDefs}
getRowId={params => params.data.id}
animateRows={true}
// For applyTransaction, initial rowData can be set,
// but subsequent changes are driven by the API.
// If you need React state to be primary, update it after applyTransaction
// or use the immutable updates method above.
></AgGridReact>
When using applyTransaction(), remember that the grid's internal data store is updated. If your React component's rowData state is meant to be the single source of truth, you'll need to manually update that state after calling applyTransaction() (e.g., by fetching the new data from the grid using gridApi.forEachNode() and updating state, or by performing the same transaction logic on your state). A common pattern is to let AG-Grid manage the data once it's initially loaded, and rely on its API for all mutations.
Best Practices and Considerations
-
Immutable Data: Always favor immutable updates for your
rowDataarray in React. This means creating new arrays and new object references for changed rows, rather than mutating existing ones. React's diffing and AG-Grid's optimizations work best with this pattern. -
getRowId: This cannot be stressed enough. ImplementgetRowIdto provide a unique identifier for each row. This is fundamental for AG-Grid to efficiently track rows during updates, enabling animations and preventing unnecessary re-renders. -
Performance for Large Datasets: For very large datasets, frequent full refreshes can be slow. Prioritize granular updates (via immutable state changes with
getRowIdorapplyTransaction) when only a small portion of the data changes. - Error Handling: When fetching data from an API, always include error handling to gracefully manage network issues or server errors.
- User Feedback: For long-running refresh/update operations, provide visual feedback to the user (e.g., loading spinners) to indicate that data is being processed.
Conclusion
Keeping your AG-Grid data fresh and accurate is a cornerstone of building dynamic React applications. Whether you're refreshing an entire dataset or making surgical updates to individual rows, AG-Grid offers robust and performant tools to get the job done. By leveraging React's state management with immutable data and understanding AG-Grid's transaction capabilities, you can ensure your grids remain responsive, efficient, and a joy for users to interact with.