Here is how the MUI documentation did it:
const rows = [/* Row Data */]
<DataGrid
rows={rows}
{/* Other Props */} /> /*[1]*/
I admit that this is an excellent way to do it. It is very efficient and DataGrid
now handles state on its own.
But it doesn’t work for my use case because rows
is dynamic and comes in over the wire. So this is how I did it:
const [rows, set_rows] = useState([])
/* Within some subroutine */
——begin
let data = await /* A network request that gets the rows */
set_rows(data)
——end
This works and renders the data fine but the problem now is that with each row commit, I must call set_rows()
to synchronize changes with the GUI. Now guess what happens each time? You guessed it, a GUI re-render.
How do I prevent this re-render?
Permit me to reiterate. In the MUI DataGrid
documentation, rows
are defined const
and passed as input to DataGrid.props.rows
making state updates efficient and handled automatically by DataGrid
.
In my case, I cannot declare it const
and hardcode it because the data is dynamic. I could technically declare it let
and mutate the variable after network response but after the DataGrid
renders, mutations are no longer detectable by the GUI. Hence I must use useState()
so that changes will be reflected in the GUI. If I use useState()
, I will have to manage state updates on my own end and the DataGrid
re-renders constantly with each state update.
[1] The sample was stripped down excessively to get straight to the point.
If I understood your issue correctly, one solution could be to separate the initial data (rows
) from the modified data (modifiedRowsRef
).
To prevent constant re-rendering of an MUI DataGrid on row commit while still accessing modified rows, you can try this:
import React, { useEffect, useState, useCallback, useRef } from "react";
import { DataGrid, GridRowModel, GridColDef, GridValidRowModel } from "@mui/x-data-grid";
interface RowData extends GridValidRowModel {
id: number;
lastName: string;
firstName: string;
age: number | null;
}
const columns: GridColDef[] = [
{ field: "id", headerName: "ID", width: 70 },
{ field: "firstName", headerName: "First name", width: 130, editable: true },
{ field: "lastName", headerName: "Last name", width: 130, editable: true },
{ field: "age", headerName: "Age", type: 'number', width: 90, editable: true },
];
const MyDataGrid: React.FC = () => {
const [rows, setRows] = useState<RowData[]>([]);
const modifiedRowsRef = useRef<{ [id: number]: RowData }>({});
// Check if component is rendered
useEffect(() => {
console.log("MyDataGrid component rendered");
});
useEffect(() => {
// Simulate an API call
setTimeout(() => {
setRows([
{ id: 1, lastName: "Snow", firstName: "Jon", age: 35 },
{ id: 2, lastName: "Lannister", firstName: "Cersei", age: 42 },
{ id: 3, lastName: "Lannister", firstName: "Jaime", age: 45 },
{ id: 4, lastName: "Stark", firstName: "Arya", age: 16 },
{ id: 5, lastName: "Targaryen", firstName: "Daenerys", age: null },
]);
}, 2000);
}, []);
// Function to get the entire data structure
const getEntireDataStructure = () => {
const mergedData = rows.map(row => modifiedRowsRef.current[row.id] || row);
console.log("Entire Data Structure:", mergedData);
return mergedData;
};
// Function to send the entire data structure to the server
const updateDataOnServer = useCallback(() => {
const updatedData = getEntireDataStructure();
console.log("Updated Data:", updatedData);
// Send updatedData to server
}, [rows]);
// Process row update
const processRowUpdate = useCallback(
(newRow: GridRowModel, oldRow: GridRowModel): GridRowModel => {
const updatedRow = newRow as RowData;
modifiedRowsRef.current = {
...modifiedRowsRef.current,
[updatedRow.id]: updatedRow,
};
// Log the current state of modifiedRowsRef
console.log("Modified Rows:", modifiedRowsRef.current);
// Update data on the server
updateDataOnServer();
return updatedRow;
},
[updateDataOnServer]
);
return (
<div style={{ height: 400, width: "100%" }}>
<DataGrid
rows={rows}
columns={columns}
initialState={{
pagination: {
paginationModel: { pageSize: 5, page: 0 },
},
}}
pageSizeOptions={[5]}
checkboxSelection
processRowUpdate={processRowUpdate}
onProcessRowUpdateError={(error) => console.error("Update error:", error)}
loading={rows.length === 0}
/>
<button onClick={getEntireDataStructure}>
Get Entire Data Structure
</button>
</div>
);
};
export default React.memo(MyDataGrid);
I assume you would like to update the server-side data on commit, and therefore I added these functions
Conclusion, any state update in ReactNode
will trigger a GUI re-render