Problem I am using AgGrid in my React application. I have multiple pages each with their own grid. I want to be able to navigate between the various pages but have the state (filters, sort, column order, etc) of the AgGrid retained when I return to a page.
What is Currently Happening Currently, what happens is that when I leave a page (using react-router-dom) and then return to it the AgGrid will render again from scratch, which results in the loss of all of the filtering and column configuration which had been performed before leaving the page. My ideal outcome is for it to be preserved so that when I navigate away from it and then return it is loaded instantaneously (because it doesn’t need to render again) and with the same configuration as the user has left it.
What I have tried I thought that I should be able to do this by lifting the AgGrid up to a parent component, similar to what you may do with a input field if you wanted it to be preserved between page changes, but it still resets every time I navigate away and come back. In the below example I should be able to move between path1 and path2 and preserve the grid on each respective page.
Other parameters I don't want to do this using getColumnState / applyColumnState because it results in a lag on load, I'd ideally like to preserve the state so it does not need to re-render at all. Additionally, when a user refreshes the entire website I would like these tables to be reset to default.
Step 1: Created a generic table component
MasterTable.js
import React, { useEffect, useState, useRef, useMemo } from "react";
import { AgGridReact } from "ag-grid-react";
import "../components/tables/ag-grid-theme-builder.css";
export default function MasterTable({ columns, rowData }) {
const gridRef = useRef();
return (
<div className="ag-theme-alpine flex-grow">
<AgGridReact
className="ag-theme-custom h-full"
ref={gridRef}
rowData={rowData}
columnDefs={columns}
sideBar={{
toolPanels: [
{
id: "filters",
labelDefault: "Filters",
labelKey: "filters",
iconKey: "filter",
toolPanel: "agFiltersToolPanel",
},
{
id: "columns",
labelDefault: "Columns",
labelKey: "columns",
iconKey: "columns",
toolPanel: "agColumnsToolPanel",
toolPanelParams: {
suppressRowGroups: true,
suppressValues: true,
},
},
],
}}
/>
</div>
);
}
Step 2: Create Table in Variable and Pass to Componentss
App.js
import React, { useState, useEffect, useRef } from "react";
import { BrowserRouter, Routes, Route, Navigate, Link } from "react-router-dom";
import Component
function App() {
const [rowData1, setRowData1] = useState([dummyRowData1]);
const [rowData2, setRowData2] = useState([dummyRowData2]);
const [colDefs1, setColDefs1] = useState([dummyColData1]);
const [colDefs2, setColDefs2] = useState([dummyColData2]);
const table1 = (
<MasterTable columns={colDefs1} rowData={rowData1} columnState= />
);
const table2 = (
<MasterTable columns={colDefs2} rowData={rowData2} columnState= />
);
return (
<>
<Routes>
<Route
path="/path1/"
element={<Component key="path1" table={table1}/>}
/>
<Route
path="/path2/"
element={<Component key="path2" table={table2}/>}
/>
</Routes>
</>
)
}
Step 3: Display Table in Component
Component.js
export default function Component({table}) {
return (
<>
{table}
</>
)
}
You can use the AgGridReact
table component's onStateUpdated
callback prop to save the current table state, e.g. it's column sort order and filtering criteria, etc, into a React state, to be passed back to each table's initialState
prop when the table remounts/rerenders after route changes.
Example:
App
// Import ag-Grid styles
import "ag-grid-community/styles/ag-grid.css"; // Core grid CSS
import "ag-grid-community/styles/ag-theme-alpine.css"; // Alpine theme CSS
import React from "react";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { AgGridReact } from "ag-grid-react";
import { rowData1, rowData2, colDefs1, colDefs2 } from "./TableData.js";
import TablePage from "./TablePage";
export default function App() {
const [tableState, setTableState] = React.useState({
table1: undefined,
table2: undefined,
});
const onTableStateUpdated =
(tableId) =>
({ state }) => {
setTableState((tableState) => ({
...tableState,
[tableId]: state,
}));
};
const table1 = (
<AgGridReact
key="table1"
rowData={rowData1}
columnDefs={colDefs1}
initialState={tableState.table1}
onStateUpdated={onTableStateUpdated("table1")}
/>
);
const table2 = (
<AgGridReact
key="table2"
rowData={rowData2}
columnDefs={colDefs2}
initialState={tableState.table2}
onStateUpdated={onTableStateUpdated("table2")}
/>
);
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Navigate to="/path1" replace />} />
<Route
path="/path1"
element={<TablePage title="Title 1">{table1}</TablePage>}
/>
<Route
path="/path2"
element={<TablePage title="Title 2">{table2}</TablePage>}
/>
</Routes>
</BrowserRouter>
);
}
TablePage
import React from "react";
import { Link, useLocation } from "react-router-dom";
export default function TablePage({ title, children }) {
const { pathname } = useLocation(); // Get the current location (path)
// Conditionally set the link to the opposite path
const oppositePath = pathname === "/path1" ? "/path2" : "/path1";
const linkText = pathname === "/path1" ? "Go to Path 2" : "Go to Path 1";
return (
<>
<h1>{title}</h1>
<div className="ag-theme-alpine" style={{ height: 400, width: "100%" }}>
{children}
</div>
<Link to={oppositePath}>{linkText}</Link>
</>
);
}