-
-
Notifications
You must be signed in to change notification settings - Fork 402
Description
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.
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.
Following the three snapshot technique (mentioned here), I noticed that there were a bunch of extra FiberNodes and Detached HTMLDivElements still on the heap.
These appear to be old row div elements that never get properly cleaned up.
Many of the extra objects created appear to be React props.
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)





