Skip to content

Memory leak in combination with react-table #196

@dwoznicki

Description

@dwoznicki

Hi, I believe I've found a memory leak related to react-virtual. I've only confirmed it occurs in combination with react-table at this point, but it may be unrelated to that project.

Steps to reproduce

I used the following table component in the screenshots below.

Table code
import React, {useRef, useMemo} from "react";
import ReactDOM from "react-dom";
import {
    useTable,
    useBlockLayout,
} from "react-table";
import {useVirtual} from "react-virtual";

const defaultGetRowHeight = () => 40;

const count = 2000;

const Header = () => <div>Header</div>;

const Cell = () => <div>cell</div>;

const Page = () => {
    const columns = useMemo(() => {
        const cols = [];
        for (let i = 0; i < 12; i++) {
            cols.push({
                id: `col_${i}`,
                Header,
                Cell,
                width: 100,
            });
        }
        return cols;
    }, []);

    const data = useMemo(() => {
        const dat = [];
        for (let i = 0; i < count; i++) {
            dat.push({ id: i });
        }
        return dat;
    }, []);

    const parentRef = useRef(null);

    const rowVirtualizer = useVirtual({
        size: count,
        parentRef,
        estimateSize: defaultGetRowHeight,
    });

    const tableProps = useTable({
        columns,
        data,
    }, useBlockLayout);

    return <div style={{margin: "6rem"}}>
        <div {...tableProps.getTableProps()} className="windowed-table" style={{height: 400, overflow: "scroll"}} ref={parentRef}>
            <div>
                {tableProps.headerGroups.map(headerGroup => <div {...headerGroup.getHeaderGroupProps()} className="tr">
                    {headerGroup.headers.map(column => <div {...column.getHeaderProps()} className="th">
                        {column.render("Header")}
                    </div>)}
                </div>)}
            </div>
            <div {...tableProps.getTableBodyProps()} style={{width: "100%", height: rowVirtualizer.totalSize, position: "relative"}}>
                {rowVirtualizer.virtualItems.map(virtualRow => {
                    const row = tableProps.rows[virtualRow.index];
                    tableProps.prepareRow(row);
                    const rowProps = row.getRowProps();
                    return <div { ...{
                        ...rowProps,
                        style: {
                            ...rowProps.style,
                            position: "absolute",
                            top: 0,
                            left: 0,
                            width: "100%",
                            height: virtualRow.size,
                            transform: `translateY(${virtualRow.start}px)`,
                            minWidth: "fit-content",
                        },
                        className: "tr",
                    }}>
                        {row.cells.map(cell => <div {...cell.getCellProps()} className="td">
                            {cell.render("Cell")}
                        </div>)}
                    </div>
                })}
            </div>
        </div>
    </div>
};

ReactDOM.render(
    <Page/>,
    document.getElementById("root")
);

On mount, with the table scrolled to the top, memory consumption of my page is ~10MB.

react-virtual-table-initial

After scrolling the table to the bottom using the mouse wheel (to make sure each row is scrolled into view and rendered at least once), the pages memory consumption jumped to 143MB. Note that there are 2000 rows in the table.

react-virtual-table-scrolled

Following the three snapshot technique (mentioned here), I noticed that there were a bunch of extra FiberNodes and Detached HTMLDivElements still on the heap.

react-virtual-summary

These appear to be old row div elements that never get properly cleaned up.

react-virtual-store-as-global

react-virtual-detached-htmldivelement

Many of the extra objects created appear to be React props.

react-virtual-object

I'm no expert on React internals or reading JS heap dumps, but this looks somewhat similar to this issue. If so, the problem may well lie upstream in React. But I will note that I don't have this problem when using react-window, so maybe there's something special that it does to avoid this?

Environment

react-virtual: 2.8.1
react-table: 7.1.0
react: 17.0.1
Google Chrome: Version 92.0.4515.107 (Official Build) (64-bit)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions