I have a problem in React with TypeScript using the Material UI library, related to the preservation of the state of two datagrids that affect their states bilaterally after a page refresh.
Goal
Have a datagrid/table where rows are selected (which are considered favorites) and these same rows are shown in another table (which is the favorites table). However, in the favorites table, the rows present can be deselected and when they are deselected, these are eliminated from the favorites table, and in the table with all the lines, where these were previously selected, they become deselected.
However, all this must happen in line with the Local Storage, which must store the identification of the favorite rows, so that the user, when visiting the page in different periods or between sessions, has access to them (datagrids') as he left them, both selected rows in the table with all rows as well the favorite rows in the favorites table.
What I got so far!
If the favorites datagrid does not exist, the selection of rows in the datagrid with all rows remains after page refresh.
However, with the existence of two datagrids, after refresh the page, none of the tables maintains the preservation of its previous state.
Conclusion
I will leave below the code where the problem in question is happening, as well as a link to the codesandbox with the mentioned code, as well as I will leave a link to a screen recording, where I will demonstrate the first example where the table (without communicating with the table of favorites) preserves its state, as well as the second example, where with the two tables this does not happen.
PeopleTables.tsx
import { Box } from "@mui/material";
import {
DataGrid,
GridColDef,
GridRowSelectionModel,
GridRowsProp,
} from "@mui/x-data-grid";
import React, { useEffect, useState } from "react";
import { People } from "../../data/people";
import { LocalStorageTypes, Person } from "../../models";
import styles from "./styles/SaveFavorites.module.scss";
export interface PeopleTablesProps {}
const allPeople: Person[] = People;
// Set up for Data Drid
const rows: GridRowsProp = allPeople;
const columns: GridColDef[] = [
{
field: "first_name",
headerName: "First Name",
width: 150,
flex: 1,
minWidth: 150,
},
{
field: "last_name",
headerName: "Last Name",
width: 100,
flex: 1,
minWidth: 100,
},
{
field: "gender",
headerName: "Gender",
width: 150,
flex: 1,
minWidth: 150,
},
{
field: "country",
headerName: "Country",
width: 150,
flex: 1,
minWidth: 150,
},
];
const PeopleTables: React.FC<PeopleTablesProps> = () => {
// Get Selected Rows from Local Storage
const storedFavorites = JSON.parse(
localStorage.getItem(LocalStorageTypes.FAVORITES) || "[]"
);
// Save id from selected Row
const [rowSelectionModel, setRowSelectionModel] =
useState<GridRowSelectionModel>(storedFavorites);
const [selectedPeople, setSelectedPeople] = useState<Person[]>([]);
// Add selected rows ID to localStorage
if (rowSelectionModel !== undefined) {
localStorage.setItem(
LocalStorageTypes.FAVORITES,
JSON.stringify(rowSelectionModel)
);
}
useEffect(() => {
// Update selectedPeople
setSelectedPeople(
rowSelectionModel.map((selectedId) => {
const row = rows.find((item) => item.id === selectedId) as Person;
const { id, ...rest } = row;
return { id, ...rest };
})
);
}, [rowSelectionModel, rows]);
return (
<>
<h2>All the people</h2>
<div style={{ height: 300, width: "100%" }}>
{/* DATAGRID WITH ALL */}
<Box
sx={{
height: "400px",
width: "100%",
"& .MuiDataGrid-columnHeaders": {
backgroundColor: "#A9A9A9",
},
}}
>
<DataGrid
checkboxSelection
onRowSelectionModelChange={(newRowSelectionModel) => {
setRowSelectionModel(newRowSelectionModel);
}}
rowSelectionModel={rowSelectionModel}
initialState={{
pagination: {
paginationModel: {
pageSize: 10,
},
},
}}
pageSizeOptions={[5, 10, 25]}
rows={rows}
columns={columns}
className={styles.savefavorites}
sx={{
"& .MuiDataGrid-columnHeaderCheckbox .MuiDataGrid-columnHeaderTitleContainer":
{
display: "none",
},
}}
/>
</Box>
{/* FAVOURITES DATAGRID */}
<h2>Favourite people</h2>
<Box
sx={{
height: "400px",
width: "100%",
"& .MuiDataGrid-columnHeaders": {
backgroundColor: "#A9A9A9",
},
}}
>
<DataGrid
checkboxSelection
onRowSelectionModelChange={(newRowSelectionModel) => {
setRowSelectionModel(newRowSelectionModel);
}}
rowSelectionModel={rowSelectionModel}
initialState={{
pagination: {
paginationModel: {
pageSize: 10,
},
},
}}
pageSizeOptions={[5, 10, 25]}
rows={selectedPeople}
columns={columns}
className={styles.savefavorites}
sx={{
"& .MuiDataGrid-columnHeaderCheckbox .MuiDataGrid-columnHeaderTitleContainer":
{
display: "none",
},
}}
/>
</Box>
</div>
</>
);
};
export default PeopleTables;
To solve all this, as well as the error that appears at the end of the video above `Warning: Cannot update a component (`PeopleTables`) while rendering a different component (`ForwardRef(DataGrid)`).`, I already tried to solve this using other posts here on stackoverflow, where there are questions about maintaining the states of tables and datagrids after page refresh, as well as resorting to GPT Chat, but without success.
I would like to know if anyone knows how to maintain the state of the two tables, which communicate with each other, when doing a page refresh?
Regarding the error itself, I was running into the same one albeit within one
DataGrid. The selected rows prop is being changed during render, which is what thesetStateduring render in the error message is referring to. You are setting the state ofselectedPeopleand it is being updated by your grid'sonRowSelectionModelChangeduring render.I was able to fix this by going back to the MUI documentation and realizing that I needed to enable
keepNonExistentRowsSelectedon my grid. The full explanation from MUI and why this is necessary can be found here: usage-with-server-side-pagniation. After adding it to your second grid in your sandbox, I was able to refresh the page and both grids still had previous selections.Updated sandbox code