React - Preserve state between tables that communicate with each other [datagrid]

536 Views Asked by At

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?

1

There are 1 best solutions below

0
imperial_dork On

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 the setState during render in the error message is referring to. You are setting the state of selectedPeople and it is being updated by your grid's onRowSelectionModelChange during render.

I was able to fix this by going back to the MUI documentation and realizing that I needed to enable keepNonExistentRowsSelected on 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