I have basic react example for learning and I use material-table in one of my ponents. Each time I change the page and re-open it (unmount and mount ponent), my ponent which contains material-table load more slowly. I share my code below.
import MaterialTable from 'material-table';
const columns = [
{ title: 'Id', field: 'id', hidden: true },
{ title: 'Username', field: 'username' },
{ title: 'Name', field: 'name' },
{ title: 'Phone', field: 'phone'}
];
const tableData = [
{
id: 1,
username: "User-1",
name: "name-1",
phone: "555 444 33 22"
},
{
id: 2,
username: "User-2",
name: "name-2",
phone: "111 222 33 44"
},
{
id: 3,
username: "User-3",
name: "name-3",
phone: "999 999 99 99"
}
];
const MTable = () => {
return (
<MaterialTable
title="Basic Search Preview"
columns={columns}
data={tableData}
options={{search: true }}
/>
)
}
export default MTable
After long search I did not find any solution, and after long try I just change the place of columns definition like below.
const MTable = () => {
const columns = [
{ title: 'Id', field: 'id', hidden: true },
{ title: 'Username', field: 'username' },
{ title: 'Name', field: 'name' },
{ title: 'Phone', field: 'phone'}
];
return (
<MaterialTable
title="Basic Search Preview"
columns={columns}
data={tableData}
options={{search: true }}
/>
)
}
This change solve my problem but I really want to learn why this happened. When I made the column definition outside of the method why memory leak and render slowed each page change. At the same time, when I moved into method what changed?
I have basic react example for learning and I use material-table in one of my ponents. Each time I change the page and re-open it (unmount and mount ponent), my ponent which contains material-table load more slowly. I share my code below.
import MaterialTable from 'material-table';
const columns = [
{ title: 'Id', field: 'id', hidden: true },
{ title: 'Username', field: 'username' },
{ title: 'Name', field: 'name' },
{ title: 'Phone', field: 'phone'}
];
const tableData = [
{
id: 1,
username: "User-1",
name: "name-1",
phone: "555 444 33 22"
},
{
id: 2,
username: "User-2",
name: "name-2",
phone: "111 222 33 44"
},
{
id: 3,
username: "User-3",
name: "name-3",
phone: "999 999 99 99"
}
];
const MTable = () => {
return (
<MaterialTable
title="Basic Search Preview"
columns={columns}
data={tableData}
options={{search: true }}
/>
)
}
export default MTable
After long search I did not find any solution, and after long try I just change the place of columns definition like below.
const MTable = () => {
const columns = [
{ title: 'Id', field: 'id', hidden: true },
{ title: 'Username', field: 'username' },
{ title: 'Name', field: 'name' },
{ title: 'Phone', field: 'phone'}
];
return (
<MaterialTable
title="Basic Search Preview"
columns={columns}
data={tableData}
options={{search: true }}
/>
)
}
This change solve my problem but I really want to learn why this happened. When I made the column definition outside of the method why memory leak and render slowed each page change. At the same time, when I moved into method what changed?
Share Improve this question asked Sep 16, 2021 at 20:13 ChivalrouSChivalrouS 2063 silver badges14 bronze badges 4- this doesn't make any sense, actually the second version should be in theory slower :) btw slow <> memory leak, there must be something else which caused this. – windmaomao Commented Sep 16, 2021 at 22:33
- Actually I thougth the same way. That is why, finding the solution took my long time. You can basically try two version of this and you will see the problem. Each time you unmount and mount the ponent, It will take more time. @windmaomao – ChivalrouS Commented Sep 16, 2021 at 22:40
- @windmaomao In addition, Its really ate my all memory :) – ChivalrouS Commented Sep 16, 2021 at 23:08
-
2
I do confirm this behavior. When mounting / unmounting repeatedly with e.g.
{ !show ? null : <MTable /> }
it took approx. 50, 70, 130, 870, 8020 milliseconds to build the MaterialTable. – kca Commented Sep 17, 2021 at 22:36
3 Answers
Reset to default 6analysis
material-table adds a property column.tableData
to each column of your columns
list, and then there is an assignment that effectively does something like (see file data-manager.js):
column[0].tableData.width = "calc(" + ... +
column[0].tableData.width + ... +
column[1].tableData.width + ... + ")"
column[1].tableData.width = "calc(" + ...
...
Because the columns are in the global scope and are not destroyed on every unmount, this lets the string tableData.width
grow exponentially. I guess the increasingly long time it takes es from these increasingly many nested "calc()" invocations.
conclusion
I hesitate to call this a bug in material-table.
It looks like material-table expects the columns to be created on every render (and not to be persistent). Fair enough, but I would at least call this unexpected behavior for somebody who is used to work in React, and there should be a warning about this in the documentation. I also think even then that could have been implemented foolproof. (if somebody disagrees, I'd like to read reasons in the ments)
example
The first time the ponent is mounted the tableData.width
is:
calc((100% - (0px +
calc((100% - (0px)) / 3) +
calc((100% - (0px)) / 3) +
calc((100% - (0px)) / 3)
)) / 3)
After un-mount and a second mount the width tableData.width
is:
calc((100% - (0px +
calc((100% - (0px +
calc((100% - (0px + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3))) / 3) +
calc((100% - (0px + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3))) / 3) +
calc((100% - (0px + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3))) / 3)
)) / 3) +
calc((100% - (0px +
calc((100% - (0px + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3))) / 3) +
calc((100% - (0px + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3))) / 3) +
calc((100% - (0px + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3))) / 3)
)) / 3) +
calc((100% - (0px +
calc((100% - (0px + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3))) / 3) +
calc((100% - (0px + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3))) / 3) +
calc((100% - (0px + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3) + calc((100% - (0px)) / 3))) / 3)
)) / 3)
)) / 3)"
The columns array should be static to a material table instance. When it is dynamically called again and again, the memory fills and finally runs out of space.
If you try to use useState to manage the columns this issue will happen. I had a workaround where I would set the columns each time which worked but would reset my groupings, etc.
There is a fork of material-table which solves that issue:https://material-table-core./docs/#installation
npx create-react-app material-table-app
npm uninstall react react-dom
npm install [email protected] [email protected]
npm install @material-table/[email protected] --legacy-deps
This is the bination that worked for me. Using this setup I can use useState to set my columns/maintain their state.
I was getting a _deepmerge issue with material-table ^5.0 that I couldn't resolve. Almost just gave up entirely on this package.