So, I have fetchData()
function that updates data everytime I clicked next or previous page button (basically, every page change). Here's the snippet
const fetchData = (limit, offset) => {
const token = localStorage.getItem("token");
const body = {
...(keyword ? { keyword } : {}),
limit,
offset,
minify: true,
};
axiosUtil
.request("POST", body.keyword ? "/blocker/private/user/popup-search" : "/blocker/private/user/popup-all", body, token)
.then((data) => {
const result = data.data ?? data;
let startTime = performance.now();
console.log("Mapping data...");
const mapped = result.map((e) => {
let color = "#36AE7C";
let text = "Enable Popup";
let icon = "CheckCircleOutlineIcon";
if (e.is_disabled != true) {
color = "#E23E57";
text = "Disabled Popup";
icon = "CancelIcon";
}
if (e.interval) {
e.type = <Chip label="Repeating Response" color="secondary" />;
} else {
e.type = <Chip label="Single Response" color="primary" />;
}
e.color = color;
e.icon = icon;
e.text = text;
e.filter_parent = Object.keys(e.filter).toString().replace(/,/g, ", ");
e.date = moment(e.createdAt).format("D MMMM YYYY");
e.action = (
<>
<ButtonGroup variant="contained" color="primary" size="small">
<Tooltip title="View Detail">
<IconButton color="primary" onClick={() => handleOpen(e.id)}>
<VisibilityIcon />
</IconButton>
</Tooltip>
<Tooltip title={e.text}>
<IconButton style={{ color: e.color }} onClick={() => handleDisable(e.id, e.is_disabled)}>
<PowerSettingsNewIcon />
</IconButton>
</Tooltip>
<Tooltip title="View Response">
<IconButton style={{ color: "#4D77FF" }} onClick={() => handleResponse(e.id, e.title, e.interval)}>
<ListAltIcon />
</IconButton>
</Tooltip>
<Tooltip title="Remove message">
<IconButton style={{ color: "#E23E57" }} onClick={() => handleRemove(e.id)}>
<DeleteOutline />
</IconButton>
</Tooltip>
</ButtonGroup>
</>
);
return e;
});
let endTime = performance.now();
let executionTime = endTime - startTime; // in milliseconds with higher precision
console.log(`Execution time: ${executionTime.toFixed(3)} ms`);
startTime = performance.now();
console.log("Setting data...");
fetchCountPopup();
unstable_batchedUpdates(() => {
setData(mapped);
});
endTime = performance.now();
executionTime = endTime - startTime; // in milliseconds with higher precision
console.log(`Execution time: ${executionTime.toFixed(3)} ms`);
})
.catch((e) => {
console.log(e);
logout();
});
};
I've logged the execution time and found out that the mapping operation is not the problem and setData()
function takes more time after changing the page several time
Then I commented data={data}
on my MaterialTable
component and the spike on setData()
's execution time is gone
<MaterialTable
className={classes.root}
icons={tableIcons}
title="Released Popup"
columns={columns}
style={{ marginTop: 10 }}
data={data}
options={{
search: false,
}}
components={{
Pagination: (props) => (
<TablePagination
{...props}
rowsPerPage={numberRowPerPage}
count={totalPopup}
page={pageNumber - 1}
onChangePage={(e, page) => handleChangePage(page + 1)}
onChangeRowsPerPage={(event) => {
props.onChangeRowsPerPage(event);
handleChangeRowPerPage(event.target.value);
}}
/>
),
}}
/>
And there's also spike on memory usage
My conclusion is it caused by the MaterialTable rerendering. How do I fix that? I'm using "material-table": "^1.68.1"
Solved by hardcoding columns
into MaterialTable
component directly since there is a memory leak bug for dynamic columns.
<MaterialTable
...
columns={[
{ title: "Title", field: "title" },
{ title: "Filter", field: "filter_parent" },
{ title: "Type", field: "type" },
{ title: "Created by", field: "created_by" },
{ title: "Created at", field: "date" },
{ title: "Action", field: "action" },
]}
style={{ marginTop: 10 }}
data={data}
options={{
search: false,
}}
...
/>
instead of
<MaterialTable
...
columns={columns}
style={{ marginTop: 10 }}
data={data}
options={{
search: false,
}}
...
/>