MUIのDataGridでServerside Paginationを実装


やりたいこと

muiのDataGridやDatatablesを使ってAPI取得した数千件のデータを編集して表示しようとすると、ブラウザが固まってしまう。

他のツールreact-data-table-componentなど色々試してみたが、同じように固まってしまうことを確認した。
結局MUIに戻り、どうやらDataGrid・Datatabelsどちらにも、server-side paginationで表示するモードに設定することができるということがわかった。
サーバー側で1ページ分の行だけを切り取って表示するようにすれば、固まったりしないらしい。

MUIのDataGridをserver-side paginationモードにする

https://mui.com/components/data-grid/pagination/#server-side-pagination
こちらの例をそのまま利用し、DemoDataのところをAPI取得データに変更

import React, { useState, useEffect, useContext } from "react";
import {
  DataGrid,
  GridColDef,
} from "@mui/x-data-grid";
import { TableData, TableDataSet } from "../../types/WebData";
import {
  setDataGrid,
  getSensor,
} from "../../api/test";
import { AuthInfoContext } from "../../context/AuthContext";
import TestDataSearchPdf from "./TestDataSearchPdf";

const columns: GridColDef[] = [
  { field: "id", headerName: "ID", hide: true },
  { field: "observe_dt", headerName: "日時", width: 200 },
  {
    field: "observe_type_name",
    headerName: "種類",
    width: 100,
    editable: false,
  },
  {
    field: "area_name",
    headerName: "エリア",
    width: 150,
    editable: false,
  },
  {
    field: "lpwa_code",
    headerName: "XX ID",
    width: 110,
    editable: false,
  },
  {
    field: "observe_value",
    flex: 1,
    headerName: "値",
    sortable: true,
    width: 300,
  },
];

/**
 * Simulates server data loading
 * 全件データを1ページ分の行にsliceしている
 * 値に単位をつけるなどのデータの編集も、ここで1ページ分のみ行う
 */
const loadServerRows = (
  page: number,
  pageSize: number,
  allRows: TableDataSet[]  // 全件データ配列
): Promise<TableDataSet[]> =>
  new Promise<TableDataSet[]>((resolve) => {
    resolve(
      // 1ページ分にsliceした後に、データを編集するfunctionに渡している
      setDataGrid(allRows.slice(page * pageSize, (page + 1) * pageSize))
    );
  });


const useQuery = (
  page: number,
  pageSize: number,
  allRows: TableDataSet[]
) => {
  const [rowCount, setRowCount] = React.useState<number | undefined>(undefined);
  const [isLoading, setIsLoading] = React.useState<boolean>(false);
  const [data, setData] = React.useState<TableDataSet[]>([]);

  React.useEffect(() => {
    let active = true;

    setIsLoading(true);
    setRowCount(undefined);
    loadServerRows(page, pageSize, allRows).then((newRows) => {
      if (!active) {
        return;
      }
      setData(newRows);
      setIsLoading(false);
      setRowCount(allRows.length);
    });

    return () => {
      active = false;
    };
  }, [page, pageSize, allRows]);

  return { isLoading, data, rowCount };
};

interface RowsState {
  page: number;
  pageSize: number;
}

const ServerDataGrid = () => {
  const [testData, setTestData] = useState<TableData>({ data: [] });
  useEffect(() => {
    const getTestData = async () => {
      // APIでデータget
      const result = await getTest();
      setTestData(result);
    };
    getTestData();
  }, []);

  // ページのstate 1ページに10行
  const [rowsState, setRowsState] = React.useState<RowsState>({
    page: 0,
    pageSize: 10,
  });

  const { isLoading, data, rowCount } = useQuery(
    rowsState.page,
    rowsState.pageSize,
    testData.data // APIで取得した全件データ
  );

  // データの読み込みが間に合ってなくてrowCountがundefinedのままの場合の対策
  const [rowCountState, setRowCountState] = React.useState(rowCount || 0);
  React.useEffect(() => {
    setRowCountState((prevRowCountState) =>
      rowCount !== undefined ? rowCount : prevRowCountState
    );
  }, [rowCount, setRowCountState]);

  return (
    <div style={{ height: 670, width: "100%" }}>
      {/* <TestDataSearchPdf exportData={testData.data} /> */}
      <DataGrid
        columns={columns}
        rows={data}
        rowCount={rowCountState}
        loading={isLoading}
        rowsPerPageOptions={[10]}
        pagination
        {...rowsState}
        paginationMode="server"
        onPageChange={(page) => setRowsState((prev) => ({ ...prev, page }))}
        onPageSizeChange={(pageSize) =>
          setRowsState((prev) => ({ ...prev, pageSize }))
        }
      />
    </div>
  );
};

export default ServerDataGrid;

課題

同じデータをreact-pdfを使って実装しているが、同様に大量ページとなるとブラウザが固まってしまう。
いい方法がないだろうか模索中。