Importing Data into AG-Grid in React
The power of a data grid lies in its ability to display information dynamically and efficiently. For AG-Grid within a React application, this means understanding how to effectively import data, whether it's a simple static array or complex data fetched asynchronously from an API. This entry, part of our AG-Grid React series, will guide you through the fundamental and advanced techniques for populating your AG-Grid component with data.
The Foundation: The rowData Prop
At its core, AG-Grid expects your data in a very specific format: an array of JavaScript objects. Each object in this array represents a row in your grid, and each property within that object corresponds to a column defined in your columnDefs. The primary way to pass this data to AG-Grid is via the rowData prop.
Let's look at a basic setup:
import React, { useState, useMemo } from 'react';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/styles/ag-grid.css'; // Core grid CSS
import 'ag-grid-community/styles/ag-theme-alpine.css'; // Theme
const SimpleGrid = () => {
// Define your column definitions
const [columnDefs] = useState([
{ field: 'make', filter: true, sortable: true },
{ field: 'model', filter: true, sortable: true },
{ field: 'price', filter: true, sortable: true }
]);
// Define your row data directly
const [rowData] = useState([
{ make: 'Toyota', model: 'Celica', price: 35000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Porsche', model: 'Boxster', price: 72000 },
{ make: 'BMW', model: 'M5', price: 60000 },
{ make: 'Mercedes', model: 'C-Class', price: 45000 },
]);
// Set grid options
const defaultColDef = useMemo(() => ({
flex: 1,
minWidth: 100,
resizable: true,
}), []);
return (
<div className="ag-theme-alpine" style={{ height: 400, width: 600 }}>
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
animateRows={true}
/>
</div>
);
};
export default SimpleGrid;
In this example, rowData is a simple array of objects, directly defined within the component. This is perfect for static data, demonstration purposes, or when your dataset is small and doesn't change frequently.
Importing Dynamic Data from APIs
Most real-world applications require data to be fetched from a backend API. In React, the useState and useEffect hooks are your best friends for handling asynchronous data loading. You'll typically initialize rowData as an empty array and then populate it once the data has been successfully fetched.
import React, { useState, useEffect, useMemo } 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 ApiDataGrid = () => {
const [rowData, setRowData] = useState([]); // Initialize rowData as an empty array
const [columnDefs] = useState([
{ field: 'id', headerName: 'ID', sortable: true },
{ field: 'title', headerName: 'Title', filter: true, sortable: true },
{ field: 'userId', headerName: 'User ID', sortable: true },
]);
const defaultColDef = useMemo(() => ({
flex: 1,
minWidth: 100,
resizable: true,
}), []);
useEffect(() => {
// Fetch data from an API
fetch('https://jsonplaceholder.typicode.com/posts?_limit=10') // Example API
.then(res => res.json())
.then(data => {
setRowData(data); // Set the fetched data to rowData state
})
.catch(error => {
console.error("Error fetching data: ", error);
});
}, []); // Empty dependency array means this effect runs once after initial render
return (
<div className="ag-theme-alpine" style={{ height: 500, width: 800 }}>
<h3>Posts from JSONPlaceholder</h3>
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
animateRows={true}
/>
</div>
);
};
export default ApiDataGrid;
In this scenario, useEffect handles the side effect of data fetching. Once the data is retrieved, setRowData updates the state, which in turn causes AG-Grid to re-render with the new data.
Enhancing Data Loading: State Management & Error Handling
When dealing with API calls, it's crucial to provide a good user experience by showing loading indicators and handling potential errors. You can extend your component's state to manage these scenarios:
loadingstate: To indicate that data is being fetched.errorstate: To display an error message if the fetch fails.
import React, { useState, useEffect, useMemo } 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 RobustApiDataGrid = () => {
const [rowData, setRowData] = useState([]);
const [loading, setLoading] = useState(true); // Add loading state
const [error, setError] = useState(null); // Add error state
const [columnDefs] = useState([
{ field: 'make', headerName: 'Car Make', filter: true, sortable: true },
{ field: 'model', headerName: 'Car Model', filter: true, sortable: true },
{ field: 'price', headerName: 'Price (USD)', filter: true, sortable: true, valueFormatter: p => '$' + p.value.toLocaleString() },
{ field: 'year', headerName: 'Year', filter: true, sortable: true },
]);
const defaultColDef = useMemo(() => ({
flex: 1,
minWidth: 100,
resizable: true,
}), []);
useEffect(() => {
const fetchCarData = async () => {
try {
setLoading(true);
setError(null); // Clear any previous errors
const response = await fetch('https://www.ag-grid.com/example-assets/row-data.json'); // Example AG-Grid data source
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setRowData(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchCarData();
}, []);
if (loading) {
return <div>Loading AG-Grid data...</div>;
}
if (error) {
return <div style={{ color: 'red' }}>Error: {error}. Please try again.</div>;
}
return (
<div className="ag-theme-alpine" style={{ height: 500, width: 800 }}>
<h3>Car Data from AG-Grid Example Assets</h3>
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
animateRows={true}
/>
</div>
);
};
export default RobustApiDataGrid;
This approach provides immediate feedback to the user and gracefully handles network issues or API failures, making your application more robust.
Data Pre-processing and Transformation
It's not uncommon for API responses to not perfectly match the structure AG-Grid expects or the column definitions you've set up. You might need to:
- Rename keys: If your API returns
car_makebut your column def ismake. - Flatten nested objects: If a column requires data from a nested property (e.g.,
car.details.engineType). - Format values: Convert raw numbers to currency, parse date strings, etc. (though
valueFormatterincolumnDefscan often handle this).
It's generally best practice to perform these transformations *before* you set the rowData in your state.
import React, { useState, useEffect, useMemo } 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 TransformedDataGrid = () => {
const [rowData, setRowData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [columnDefs] = useState([
{ field: 'id', headerName: 'Product ID', sortable: true },
{ field: 'productName', headerName: 'Product Name', filter: true, sortable: true },
{ field: 'category', headerName: 'Category', filter: true, sortable: true },
{ field: 'price', headerName: 'Unit Price', sortable: true, valueFormatter: p => '$' + p.value.toLocaleString() },
]);
const defaultColDef = useMemo(() => ({
flex: 1,
minWidth: 100,
resizable: true,
}), []);
useEffect(() => {
const fetchAndTransformData = async () => {
try {
setLoading(true);
setError(null);
// Simulate an API response with slightly different keys and a nested structure
const rawData = [
{ item_id: 1, item_name: 'Laptop Pro', details: { type: 'Electronics', cost: 1200 }, stock: 50 },
{ item_id: 2, item_name: 'Mechanical Keyboard', details: { type: 'Peripherals', cost: 150 }, stock: 200 },
{ item_id: 3, item_name: 'USB-C Hub', details: { type: 'Accessories', cost: 75 }, stock: 1000 },
];
// Transform the raw data to match columnDefs
const transformedData = rawData.map(item => ({
id: item.item_id,
productName: item.item_name,
category: item.details.type,
price: item.details.cost,
// You might choose to ignore 'stock' if not needed in the grid
}));
setRowData(transformedData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchAndTransformData();
}, []);
if (loading) {
return <div>Loading & transforming product data...</div>;
}
if (error) {
return <div style={{ color: 'red' }}>Error: {error}.</div>;
}
return (
<div className="ag-theme-alpine" style={{ height: 400, width: 800 }}>
<h3>Transformed Product Data</h3>
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
animateRows={true}
/>
</div>
);
};
export default TransformedDataGrid;
This approach gives you full control over how your data is presented in the grid, ensuring consistency and accuracy.
Performance Considerations for Large Datasets
For most applications, passing an array of objects to the rowData prop (client-side row model) is sufficient and highly performant for hundreds or even a few thousand rows. However, when dealing with extremely large datasets (tens of thousands to millions of rows), you might encounter performance bottlenecks.
In such advanced scenarios, AG-Grid offers specialized row models that are optimized for scale:
- Infinite Row Model: For lazy loading data as the user scrolls, fetching only what's visible.
- Server-Side Row Model: The most powerful model, offloading data operations (filtering, sorting, grouping) entirely to the server, suitable for enterprise-scale applications.
While these row models involve more complex setup for data fetching (often requiring custom data source implementations), the fundamental principle of preparing data as an array of objects remains, just in smaller, managed chunks.