-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
DataTable: Memory leak when data updated continuously #5927
Comments
(
It should be here, Many cells are rendered and create man listeners After commenting, the memory is stable and gc can be seen |
Awesome work @kl-nevermore I will test today! |
@tracktor-git i can't reach your Code Sandbox? Is this an editable datatable? |
@melloware import { DocSectionCode } from '@/components/doc/common/docsectioncode';
import { DocSectionText } from '@/components/doc/common/docsectiontext';
import { Column } from '@/components/lib/column/Column';
import { DataTable } from '@/components/lib/datatable/DataTable';
import { useState, useEffect } from 'react';
import { ProductService } from '../../../service/ProductService';
import DeferredDemo from '@/components/demo/DeferredDemo';
import colsData from './colsData.json';
import { random, sample, sampleSize, times, isEmpty } from 'lodash';
const ROWS_NUM = 50;
const COLUMNS_TO_UPDATE = 5;
const generateRandomCellValue = (length) => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return times(length, () => sample(characters)).join('');
};
const generateRows = (rowsNum, columnsData) => {
const rows = [];
for (let i = 0; i < rowsNum; i++) {
let row = {};
columnsData.forEach((col) => {
row[col.field] = random(0, 1000000);
});
rows.push(row);
}
return rows;
};
export function BasicDoc(props) {
const [products, setProducts] = useState([]);
const [rows, setRows] = useState([]);
useEffect(() => {
//Initialise table with generated rows
const newRows = generateRows(ROWS_NUM, colsData);
setRows(newRows);
const interval = setInterval(() => {
setRows((currentRows) => {
if (!isEmpty(currentRows)) {
const updatedData = [...currentRows];
// Determin random row and row columns to be updated
const randomRow = random(0, ROWS_NUM - 1);
const randomColumns = sampleSize(colsData, COLUMNS_TO_UPDATE);
randomColumns.forEach(({ field }) => {
// update cell with random data
updatedData[randomRow][field] = generateRandomCellValue(6);
});
return updatedData;
}
});
}, 1000);
return () => clearInterval(interval);
}, []);
const loadDemoData = () => {
ProductService.getProductsMini().then((data) => setProducts(data));
};
const code = {
basic: `
<DataTable value={products} tableStyle={{ minWidth: '50rem' }}>
<Column field="code" header="Code"></Column>
<Column field="name" header="Name"></Column>
<Column field="category" header="Category"></Column>
<Column field="quantity" header="Quantity"></Column>
</DataTable>
`,
javascript: `
import React, { useState, useEffect } from 'react';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { ProductService } from './service/ProductService';
export default function BasicDemo() {
const [products, setProducts] = useState([]);
useEffect(() => {
ProductService.getProductsMini().then(data => setProducts(data));
}, []);
return (
<div className="card">
<DataTable value={products} tableStyle={{ minWidth: '50rem' }}>
<Column field="code" header="Code"></Column>
<Column field="name" header="Name"></Column>
<Column field="category" header="Category"></Column>
<Column field="quantity" header="Quantity"></Column>
</DataTable>
</div>
);
}
`,
typescript: `
import React, { useState, useEffect } from 'react';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { ProductService } from './service/ProductService';
interface Product {
id: string;
code: string;
name: string;
description: string;
image: string;
price: number;
category: string;
quantity: number;
inventoryStatus: string;
rating: number;
}
export default function BasicDemo() {
const [products, setProducts] = useState<Product[]>([]);
useEffect(() => {
ProductService.getProductsMini().then(data => setProducts(data));
}, []);
return (
<div className="card">
<DataTable value={products} tableStyle={{ minWidth: '50rem' }}>
<Column field="code" header="Code"></Column>
<Column field="name" header="Name"></Column>
<Column field="category" header="Category"></Column>
<Column field="quantity" header="Quantity"></Column>
</DataTable>
</div>
);
}
`,
data: `
{
id: '1000',
code: 'f230fh0g3',
name: 'Bamboo Watch',
description: 'Product Description',
image: 'bamboo-watch.jpg',
price: 65,
category: 'Accessories',
quantity: 24,
inventoryStatus: 'INSTOCK',
rating: 5
},
...
`
};
return (
<>
<DocSectionText {...props}>
<p>
DataTable requires a <i>value</i> as data to display and <i>Column</i> components as children for the representation.
</p>
</DocSectionText>
<DeferredDemo onLoad={loadDemoData}>
<div className="card">
<DataTable value={rows} tableStyle={{ minWidth: '50rem' }}>
{colsData && colsData.map((col) => <Column key={col.field} field={col.field} header={col.header}></Column>)}
</DataTable>
</div>
</DeferredDemo>
<DocSectionCode code={code} service={['ProductService']} />
</>
);
} |
OK @kl-nevermore can you try my PR? Those listeners should not be registering unless the cell is editable. |
OK, I'll go home later to test |
OK but now at least is it only called when the cell is edtiable right so not for normal datatables? |
No, there will be problems as long as I also found |
i thought the |
No, it is not editable. It is sortable, if it's important. I see, you already found the trouble... |
|
Hmm but usePrevious is just holding a ref? export const usePrevious = (newValue) => {
const ref = React.useRef(undefined);
React.useEffect(() => {
ref.current = newValue;
});
return ref.current;
}; so is it that the React.useEffect(() => {
// to properly compare functions we can implicitly converting the function to it's body's text as a String
if (listenerRef.current && ('' + prevListener !== '' + listener || prevOptions !== options)) {
unbind();
when && bind();
}
}, [listener, options, when]); |
unbind called,but React.useEffect(() => {
// to properly compare functions we can implicitly converting the function to it's body's text as a String
if (listenerRef.current && ('' + prevListener !== '' + listener || prevOptions !== options)) {
unbind();
when && bind();
}
return () => {
prevListener = null
}
}, [listener, options, when]); |
OK @kl-nevermore can you review my updated PR? |
I moved the |
Actually I also updated the |
|
So I guess we should release manually when not needed like, if (listenerRef.current && ('' + prevListener !== '' + listener || prevOptions !== options)) {
unbind();
when && bind();
}else{
// dispose
} |
@kl-nevermore made one more fix if you want to try again. |
I had fever yesterday. I just tested it and it fixed! |
Describe the bug
Hi there!
In the DataTable component, when constantly updating data (for example, via a websocket), the browser quickly begins to eat up RAM. This problem was already identified earlier, but remained unresolved (issue #4656). Version 9.5.0 does not have this problem.
Reproducer
https://96vrp7-5173.csb.app/
PrimeReact version
10.5.0
React version
18.x
Language
ES6
Build / Runtime
Vite
Browser(s)
Firefox 122.0.1, Maxthon 7.1.8.6000, Chrome latest
Steps to reproduce the behavior
Expected behavior
No response
The text was updated successfully, but these errors were encountered: