Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leak in combination with react-table #196

Closed
dwoznicki opened this issue Sep 17, 2021 · 3 comments
Closed

Memory leak in combination with react-table #196

dwoznicki opened this issue Sep 17, 2021 · 3 comments

Comments

@dwoznicki
Copy link

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)

@piecyk
Copy link
Collaborator

piecyk commented Sep 17, 2021

Ooo good catch 👍 Manage to reproduce it, happens in react developer tools in context of hooks, also in production build. I think you didn't observe it with react-window because it's build using class components.

Trying to pin point where it leaks, for now disable the developer tools.

@piecyk
Copy link
Collaborator

piecyk commented Sep 17, 2021

@dwoznicki this will be fixed ( via facebook/react#22346 ) in next DevTools release.

@dwoznicki
Copy link
Author

Thanks for your help with thinks @piecyk. I've confirmed that disabling React DevTools does indeed solve the leak.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants