JP Calvo
JP Calvo

Reputation: 331

Material-UI: DataGrid server-side pagination issue when setting rowCount

So I just started using Material UI and pretty much I am loving it. Now, we are working on a project that involves data from users, employees and addresses in a specific city here in Philippines and I decided to use a table to display it to the client since I find this much easier. So, this table needs to be paginated, sorted, filtered, etc. and we are doing it in the server's side. Apparently, client needs to send couple of data like { page: 1, page_size: 50, ....} and that's what we did in my react.

The problem that I have right now is I think it is with the DataGrid. I think the table does not re-render the rowCount after fetching the totalRows data in the database. I have the sandbox here (PS: You have to enlarge the output screen to have the rowsPerPageOptions visible.) But as you can notice in there the first time it loads the next arrow is disabled and it does not re-render the time the actual data including the number of rows was loaded. But if you keep navigating like changing the page size it goes available like nothing is wrong.

I'm kind of stuck with this issue right now and I don't even know if I am using it the right way. Any help will be appreciated.

import { useState, useEffect } from "react";
import { DataGrid } from "@material-ui/data-grid";
import { Box } from "@material-ui/core";

const dummyColorsDB = [
  { id: 1, color: "red" },
  { id: 2, color: "green" },
  { id: 3, color: "blue" },
  { id: 4, color: "violet" },
  { id: 5, color: "orange" },
  { id: 6, color: "burgundy" },
  { id: 7, color: "pink" },
  { id: 8, color: "yellow" },
  { id: 9, color: "magenta" },
  { id: 10, color: "random color" },
  { id: 11, color: "another random color" },
  { id: 12, color: "last one" }
];

export default function App() {
  const [data, setData] = useState({
    loading: true,
    rows: [],
    totalRows: 0,
    rowsPerPageOptions: [5, 10, 15],
    pageSize: 5,
    page: 1
  });

  const updateData = (k, v) => setData((prev) => ({ ...prev, [k]: v }));

  useEffect(() => {
    updateData("loading", true);

    setTimeout(() => {
      const rows = dummyColorsDB.slice(
        (data.page - 1) * data.pageSize,
        (data.page - 1) * data.pageSize + data.pageSize
      );

      console.log(rows);

      updateData("rows", rows);
      updateData("totalRows", dummyColorsDB.length);
      updateData("loading", false);
    }, 500);
  }, [data.page, data.pageSize]);

  return (
    <Box p={5}>
      <DataGrid
        density="compact"
        autoHeight
        rowHeight={50}
        //
        pagination
        paginationMode="server"
        loading={data.loading}
        rowCount={data.totalRows}
        rowsPerPageOptions={data.rowsPerPageOptions}
        page={data.page - 1}
        pageSize={data.pageSize}
        rows={data.rows}
        columns={[{ field: "color", headerName: "Color", flex: 1 }]}
        onPageChange={(data) => {
          updateData("page", data.page + 1);
        }}
        onPageSizeChange={(data) => {
          updateData("page", 1);
          updateData("pageSize", data.pageSize);
        }}
      />
    </Box>
  );
}

Upvotes: 7

Views: 19013

Answers (1)

NearHuscarl
NearHuscarl

Reputation: 81380

When you update the component state by calling multiple times like this:

updateData("rows", rows);
updateData("rowCount", dummyColorsDB.length);
updateData("loading", false);

Your updateData calls setState but because setState executes asynchronously, they are not updated at the same time. In fact, the reason why the pagination doesn't work at the first render is because you set the grid rows before setting its rowCount. My guess is that this is a Material-UI bug after inspecting the codebase. They don't seem to add state.options.rowCount to the dependency array in useEffect so nothing get re-render when you update rowCount later.

This is clearer when you defer each call a little bit. The code below does not work.

// set rows first
updateData("rows", rows);

setTimeout(() => {
  // set rowCount later
  updateData("rowCount", dummyColorsDB.length);
  updateData("loading", false);
}, 100);

But try setting the rowCount first and the pagination works again

// set rowCount first
updateData("rowCount", dummyColorsDB.length);

setTimeout(() => {
  updateData("rows", rows);
  updateData("loading", false);
}, 100);

Another solution is to update all related state at the same time:

setData((d) => ({
  ...d,
  rowCount: dummyColorsDB.length,
  rows,
  loading: false
}));

Live Demo

Edit 66950096/material-ui-datagrid-server-rendering-issue

Upvotes: 11

Related Questions