mui / mui-x

MUI X: Build complex and data-rich applications using a growing list of advanced React components, like the Data Grid, Date and Time Pickers, Charts, and more!
https://mui.com/x/
4.52k stars 1.31k forks source link

Datagrid Server-side pagination causes memory leak #8922

Open Washbear10 opened 1 year ago

Washbear10 commented 1 year ago

Duplicates

Latest version

Steps to reproduce 🕹

Link to live example:

Steps:

  1. Create Datagrid and supply with a set of 3 rows
  2. Generate new set on page change
  3. Set the rows state where Datagrid gets it's row data from to the new set
  4. Check in browsers memory snapshot how many rows are still in memory

Here is a code snippet to reproduce and screenshots:


import * as React from "react";
import Box from "@mui/material/Box";
import { DataGrid } from "@mui/x-data-grid";
import { v4 } from "uuid";

const columns = [
    { field: "id", headerName: "ID", width: 90 },
    {
        field: "firstName",
        headerName: "First name",
        width: 150,
        editable: true,
    },
    {
        field: "lastName",
        headerName: "Last name",
        width: 150,
        editable: true,
    },
    {
        field: "age",
        headerName: "Age",
        type: "number",
        width: 110,
        editable: true,
    },
    {
        field: "fullName",
        headerName: "Full name",
        description: "This column has a value getter and is not sortable.",
        sortable: false,
        width: 160,
        valueGetter: (params) =>
            `${params.row.firstName || ""} ${params.row.lastName || ""}`,
    },
];

class TestRow {
    constructor({ id, lastName, firstName, age }) {
        this.id = id;
        this.lastName = lastName;
        this.firstName = firstName;
        this.age = age;
    }
}

const startRows = [
    new TestRow({
        id: v4(),
        lastName: (Math.random() + 1).toString(36).substring(7),
        firstName: (Math.random() + 1).toString(36).substring(7),
        age: Math.floor(Math.random() * 100),
    }),
    new TestRow({
        id: v4(),
        lastName: (Math.random() + 1).toString(36).substring(7),
        firstName: (Math.random() + 1).toString(36).substring(7),
        age: Math.floor(Math.random() * 100),
    }),
    new TestRow({
        id: v4(),
        lastName: (Math.random() + 1).toString(36).substring(7),
        firstName: (Math.random() + 1).toString(36).substring(7),
        age: Math.floor(Math.random() * 100),
    }),
];

export default function DataGridDemo() {
    const [rows, setRows] = React.useState(startRows);
    const [paginationModel, setPaginationModel] = React.useState({
        page: 0,
        pageSize: 3,
    });
    React.useEffect(() => {
        let n = [];
        for (let i = 0; i < 3; i++) {
            n.push(
                new TestRow({
                    id: v4(),
                    lastName: (Math.random() + 1).toString(36).substring(7),
                    firstName: (Math.random() + 1).toString(36).substring(7),
                    age: Math.floor(Math.random() * 100),
                })
            );
        }
        setRows(n);
    }, [paginationModel.page]);
    return (
        <Box sx={{ height: 400, width: "100%" }}>
            <DataGrid
                rows={rows}
                columns={columns}
                onPageChange={(newPage) => {
                    setPaginationModel({ ...paginationModel, page: newPage });
                }}
                rowsPerPageOptions={[3]}
                pageSize={3}
                checkboxSelection
                disableRowSelectionOnClick
                paginationModel={paginationModel}
                paginationMode="server"
                pagination
            />
        </Box>
    );
}

image_2023-05-08_141234937 image_2023-05-08_141336837 image_2023-05-08_141501333

Current behavior 😯

When setting the paginationMode to server, any rows that were once displayed by the datagrid on a page are not being garbage-collected. Every time switching to the next page, the new rows are added to the memory heap, but old rows are still in memory, even without any reference to them.

I have hundreds, maybe thousands of rows possibly being inspected by the user, some of them containing a lot of data (image data) . They cannot all be stored on the client memory.

Expected behavior 🤔

When passing new rows to the datagrid, old rows should be garbage collected.

Context 🔦

I am fetching the next set of rows from an api. I want to increase memory performance by not having the client store all row pages from the start, but only the rows currently passed to Datagrid.

Your environment 🌎

System: OS: Windows 10 10.0.19045 Binaries: Node: 18.14.0 - C:\Program Files\nodejs\node.EXE Yarn: Not Found npm: 9.4.2 - C:\Program Files\nodejs\npm.CMD Browsers: Chrome: Not Found Edge: Spartan (44.19041.1266.0), Chromium (113.0.1774.35) npmPackages: @emotion/react: ^11.10.5 => 11.10.5 @emotion/styled: ^11.10.5 => 11.10.5 @mui/base: 5.0.0-alpha.113 @mui/core-downloads-tracker: 5.11.4 @mui/icons-material: ^5.11.0 => 5.11.0 @mui/material: ^5.11.4 => 5.11.4 @mui/private-theming: 5.11.2 @mui/styled-engine: 5.11.0 @mui/system: 5.11.4 @mui/types: 7.2.3 @mui/utils: 5.11.2 @mui/x-data-grid: ^5.17.19 => 5.17.19 @mui/x-date-pickers: ^5.0.20 => 5.0.20 @types/react: 18.0.26 react: ^18.2.0 => 18.2.0 react-dom: ^18.2.0 => 18.2.0 typescript: 4.9.5

Order ID or Support key 💳 (optional)

No response

m4theushw commented 1 year ago

Here's a CodeSandbox showing the bug in v6: https://codesandbox.io/s/wild-http-ku2sod?file=/demo.tsx

From what I saw this seems to be related to the cache of the selectors still holding references to the old rows. We need to investigate more;