daveSurina
daveSurina

Reputation: 21

Material-UI DataGrid/DataGridPro: How to persist state of columns through re-render, when you change their visibility with DataGrid Column Toolbar

We are using MUI DataGrid in our React based project.

At the moment I am trying to save/persist state of columns after toggling visibility of some columns with DataGrid's toolbar column menu, as currently after re-render it is back to default column setup.

I would like to know how could I access state of DataGrid/visibility state of columns in DataGrid so I can adjust/save it/reuse it later?

So far I meddled a bit with a apiRef, but all I got from apiRef.current was empty object. I am adding below some basic codeSandbox example to show how I tried to access it.

https://codesandbox.io/s/datagridprodemo-material-demo-forked-189j9?file=/demo.js


Maybe there is better/different approach, or I just need to create the state somehow. We would like to persist the state of the columns as user preference possibly in a future so this is vital for that to happen.

All suggestions are welcome and I thank you beforehand.

Upvotes: 2

Views: 4977

Answers (2)

rpggio
rpggio

Reputation: 2545

I made a hook to persist column settings to localStorage. It uses callbacks on the API ref object. Usage:

function MyGrid() {
  const apiRef = useGridApiRef()
  usePersistColumnSettings(apiRef, 'customers-grid')
  return <DataGrid apiRef={apiRef} />
}

Upvotes: 3

John-moen
John-moen

Reputation: 21

Fortunately, the DataGrid API provides the columnVisibilityModel and onColumnVisibilityChange props.

See this code sandbox for a simple example of controlling the columnVisibilityModel: https://codesandbox.io/s/mui-datagrid-oncolumnvisibilitychange-savestate-u1opzc?file=/src/App.tsx:1960-1984

Here is the code for a simple implementation. Your initial state may vary. Also, note that I could not figure out how to get DataGridPro to call onColumnVisibilityChange unless columnVisibilityModel was initially undefined. Bug, or my mistake, I am uncertain.

import "./styles.css";
import React from "react";
import {
  DataGrid,
  GridRowsProp,
  GridColDef,
  GridCallbackDetails,
  MuiEvent,
  GridColumnVisibilityModel,
  GridColumnVisibilityChangeParams
} from "@mui/x-data-grid";
import { Button } from "@mui/material";

const rows: GridRowsProp = [
  { id: 1, col1: "Hello", col2: "World" },
  { id: 2, col1: "DataGridPro", col2: "is Awesome" },
  { id: 3, col1: "MUI", col2: "is Amazing" }
];

const columns: GridColDef[] = [
  { field: "col1", headerName: "Column 1", width: 150 },
  { field: "col2", headerName: "Column 2", width: 150 }
];

const initialVisibilityModel = { col1: true, col2: true };

export default function App() {
  // it is strange, but in order for DataGridPro to call onColumnVisibilityChange, columnVisibilityModel must be undefined initially
  const [
    currentGridColumnVisibilityModel,
    setCurrentGridColumnVisibilityModel
  ] = React.useState<GridColumnVisibilityModel | undefined>(undefined);

  const [mySavedValue, setMySavedValue] = React.useState<
    GridColumnVisibilityModel | undefined
  >(undefined);

  const onColumnVisibilityChange = React.useCallback(
    (
      params: GridColumnVisibilityChangeParams,
      event: MuiEvent<{}>,
      details: GridCallbackDetails
    ): void => {
      console.log("params", params);
      setCurrentGridColumnVisibilityModel((s) => ({
        // per the DataGridPro strangeness, we must marry in initial state only the first update
        ...(s ? s : initialVisibilityModel),
        [params.field]: params.isVisible
      }));
    },
    []
  );

  const saveACopyOfGridState = () => {
    setMySavedValue(currentGridColumnVisibilityModel || initialVisibilityModel);
  };

  const loadSavedCopyOfGridState = () => {
    setCurrentGridColumnVisibilityModel(mySavedValue || initialVisibilityModel);
  };

  const currentVisibilityAsText =
    `${Object.keys(currentGridColumnVisibilityModel ?? {}).map(
      (key) => `{${key}:${currentGridColumnVisibilityModel?.[key]}}`
    )}` || "empty";

  const savedVisibilityAsText =
    `${Object.keys(mySavedValue ?? {}).map(
      (key) => `{${key}:${mySavedValue?.[key]}}`
    )}` || "empty";

  return (
    <div style={{ height: 300, width: "100%" }}>
      <DataGrid
        rows={rows}
        columns={columns}
        columnVisibilityModel={currentGridColumnVisibilityModel}
        onColumnVisibilityChange={onColumnVisibilityChange}
      />
      <div>
        <Button onClick={saveACopyOfGridState} variant="contained">
          SAVE CURRENT COLUMN VISIBILITY STATE
        </Button>
        <Button
          onClick={loadSavedCopyOfGridState}
          variant="contained"
          color="warning"
        >
          LOAD SAVED COLUMN VISIBILITY STATE
        </Button>
      </div>
      <p>Current filter state: {currentVisibilityAsText}</p>
      <p>Saved filter state: {savedVisibilityAsText}</p>
    </div>
  );
}

Upvotes: 2

Related Questions