-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
[data grid] Problem getting latest state of row in edit mode: Have there been changes to how the gridEditRowsStateSelector()
works?
#14967
Comments
gridEditRowsStateSelector()
works?gridEditRowsStateSelector()
works?
to ensure reactivity, you need to retrieve state updates with useGridSelector hook If you update getActions: (params: GridRowParams<Article>) => {
const isInEditMode =
rowModesModel[params.id]?.mode === GridRowModes.Edit;
const rowState = useGridSelector(apiRef, gridEditRowsStateSelector)[
params.id
];
const rowHasEmptyFields = (editRow: GridEditRowProps) =>
!editRow?.author.value ||
!editRow?.articleTitle.value ||
!editRow?.rating.value;
if (isInEditMode) {
return [
<GridActionsCellItem
key="Save"
icon={<CheckIcon />}
label="Save"
onClick={handleSaveClick(params.id)}
disabled={rowHasEmptyFields(rowState)}
/>,
];
}
return [
<GridActionsCellItem
icon={<EditIcon />}
label="Edit"
key="Edit"
onClick={handleEditClick(params.id)}
/>,
];
}, your action button should behave as expected. Hope that this helps |
Good morning @arminmeh , thanks so much for the reply. Your solution with
I'm working through a solution, but wondering if you have any quick pointers for how to resolve this? |
you can try extracting the other possibility is to use |
Thanks for the tip--I'm working on extracting the
we have |
UPDATE: Just found the place in the docs saying |
@arminmeh I believe I'm in the clear now. I was able to implement a custom component for the action buttons and pass the row model, edit/save handlers, and GridRowParams to it. The StackBlitz demo is fighting me right now, so for anyone else that is interested in the solution, the code snippet is below. This code snippet has the proper behavior I appreciate your quick response to this question--I may return in a little as I have yet to implement in this approach in our actual application, and may run into other issues. I'll update to confirm that all is well. import { Box } from '@mui/material';
import {
DataGridPro,
GridActionsCellItem,
GridColDef,
GridEditRowProps,
gridEditRowsStateSelector,
GridRowId,
GridRowModes,
GridRowModesModel,
useGridApiContext,
useGridApiRef,
useGridSelector,
} from '@mui/x-data-grid-pro';
import CheckIcon from '@mui/icons-material/Check';
import EditIcon from '@mui/icons-material/Edit';
import { useState } from 'react';
import React from 'react';
import { GridRenderCellParams } from '@mui/x-data-grid';
type Article = {
articleId: string;
articleTitle: string | null;
author: string | null;
rating: 'Excellent' | 'Good' | 'Fair' | 'Poor' | null;
};
function ActionButtons(props: {
params: GridRenderCellParams<Article>;
rowModesModel: GridRowModesModel;
onSaveClick: (id: GridRowId) => void;
onEditClick: (id: GridRowId) => void;
}) {
const { params, rowModesModel, onEditClick, onSaveClick } = props;
const apiRef = useGridApiContext();
const isInEditMode = rowModesModel[params.id]?.mode === GridRowModes.Edit;
const rowState = useGridSelector(apiRef, gridEditRowsStateSelector)[
params.id
];
const rowHasEmptyFields = (editRow: GridEditRowProps) =>
!editRow?.author.value ||
!editRow?.articleTitle.value ||
!editRow?.rating.value;
if (isInEditMode) {
return [
<GridActionsCellItem
key="Save"
icon={<CheckIcon />}
label="Save"
onClick={() => onSaveClick(params.id)}
disabled={rowHasEmptyFields(rowState)}
/>,
];
}
return [
<GridActionsCellItem
icon={<EditIcon />}
label="Edit"
key="Edit"
onClick={() => onEditClick(params.id)}
/>,
];
}
export default function ExampleDataGrid() {
const apiRef = useGridApiRef();
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
const handleEditClick = (id: GridRowId) => {
console.log('edting');
setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } });
};
const handleSaveClick = (id: GridRowId) => {
console.log('saving');
setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } });
};
// const rowState = useGridSelector(apiRef, gridEditRowsStateSelector);
const rows: Article[] = [
{
articleTitle: 'How to refry beans',
author: 'John Smith',
rating: 'Excellent',
articleId: 'a1',
},
{
articleTitle: 'How to roast asparagus',
author: 'Angela Myers',
rating: 'Good',
articleId: 'b2',
},
{
articleTitle: 'Five times you were wrong about me',
author: null,
rating: null,
articleId: 'c3',
},
];
const columns: GridColDef[] = [
{
field: 'articleTitle',
headerName: 'Article Title',
flex: 1,
editable: true,
},
{
field: 'author',
headerName: 'Author',
flex: 1,
editable: true,
},
{
field: 'rating',
headerName: 'Rating',
editable: true,
},
{
field: 'actions',
type: 'actions',
renderCell: (params: GridRenderCellParams<Article>) => (
<ActionButtons
params={params}
rowModesModel={rowModesModel}
onSaveClick={() => handleSaveClick(params.id)}
onEditClick={() => handleEditClick(params.id)}
/>
),
},
];
return (
<Box>
<DataGridPro
columns={columns}
rows={rows}
getRowId={(row: Article) => row.articleId}
rowModesModel={rowModesModel}
editMode="row"
apiRef={apiRef}
disableRowSelectionOnClick
/>
</Box>
);
} |
this was a mistake you can just get it inside the component that you have made (like you did already) since you already switched to Also, splitting the component simplifies the props you need to render them and you can memoize them to prevent unnecessary re-rendering Here is the re-worked version of your last code import { Box } from '@mui/material';
import {
DataGridPro,
GridActionsCellItem,
GridColDef,
gridEditRowsStateSelector,
GridRowId,
GridRowModes,
GridRowModesModel,
useGridApiContext,
useGridApiRef,
useGridSelector,
} from '@mui/x-data-grid-pro';
import CheckIcon from '@mui/icons-material/Check';
import EditIcon from '@mui/icons-material/Edit';
import { useState } from 'react';
import React from 'react';
import { GridRenderCellParams } from '@mui/x-data-grid';
type Article = {
articleId: string;
articleTitle: string | null;
author: string | null;
rating: 'Excellent' | 'Good' | 'Fair' | 'Poor' | null;
};
function ActionEditButtonRaw(props: { rowId: GridRowId; onClick: (id: GridRowId) => void }) {
const { rowId, onClick } = props;
const apiRef = useGridApiContext();
const rowState = useGridSelector(apiRef, gridEditRowsStateSelector)[rowId];
const rowHasEmptyFields =
!rowState?.author.value || !rowState?.articleTitle.value || !rowState?.rating.value;
return (
<GridActionsCellItem
icon={<CheckIcon />}
label="Save"
onClick={() => onClick(rowId)}
disabled={rowHasEmptyFields}
/>
);
}
const ActionEditButton = React.memo(ActionEditButtonRaw);
function ActionButtonRaw(props: { rowId: GridRowId; onClick: (id: GridRowId) => void }) {
const { rowId, onClick } = props;
return <GridActionsCellItem icon={<EditIcon />} label="Edit" onClick={() => onClick(rowId)} />;
}
const ActionButton = React.memo(ActionButtonRaw);
export default function ExampleDataGrid() {
const apiRef = useGridApiRef();
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
const handleEditClick = (id: GridRowId) => {
console.log('edting');
setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } });
};
const handleSaveClick = (id: GridRowId) => {
console.log('saving');
setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } });
};
const rows: Article[] = [
{
articleTitle: 'How to refry beans',
author: 'John Smith',
rating: 'Excellent',
articleId: 'a1',
},
{
articleTitle: 'How to roast asparagus',
author: 'Angela Myers',
rating: 'Good',
articleId: 'b2',
},
{
articleTitle: 'Five times you were wrong about me',
author: null,
rating: null,
articleId: 'c3',
},
];
const columns: GridColDef[] = [
{
field: 'articleTitle',
headerName: 'Article Title',
flex: 1,
editable: true,
},
{
field: 'author',
headerName: 'Author',
flex: 1,
editable: true,
},
{
field: 'rating',
headerName: 'Rating',
editable: true,
},
{
field: 'actions',
type: 'actions',
editable: true,
renderCell: (params: GridRenderCellParams<Article>) => (
<ActionButton rowId={params.id} onClick={handleEditClick} />
),
renderEditCell: (params: GridRenderCellParams<Article>) => (
<ActionEditButton rowId={params.id} onClick={handleSaveClick} />
),
},
];
return (
<Box>
<DataGridPro
columns={columns}
rows={rows}
getRowId={(row: Article) => row.articleId}
rowModesModel={rowModesModel}
editMode="row"
apiRef={apiRef}
disableRowSelectionOnClick
/>
</Box>
);
} |
Nice, appreciate the improvement on the example solution. We are able to move forward now, I'll close this issue. Thank you again for the prompt help. |
This issue has been closed. If you have a similar problem but not exactly the same, please open a new issue. Note @snarky-barnacle How did we do? Your experience with our support team matters to us. If you have a moment, please share your thoughts in this short Support Satisfaction survey. |
The problem in depth
We recently updated our version of the DataGridPro to 7.20.0 from v5. In doing so, we have found that one of the selectors we use to do custom validation and control of the row mode no longer works as expected. We think we may have found a bug in the DataGridPro component, but wondering if there's another way to accomplish what we're trying to do?
In our application we use row editing, and have edit/save buttons in an actions column to control the row mode. We only allow saving row edits if all cell validation criteria have been met.
To do this, we use
gridEditRowsStateSelector()
as part of thegetActions
function in the columns definition. We get the latest state of the edited row, then use a validation utility on that state that returns true/false, allowing us to enable/disable the save button, like so:The problem: we found that when filling in a row, even though all cell criteria are met, the save button would not enable. Upon closer look, we discovered that the DataGrid state wasn't updating after each cell edit--this was done by logging the output
gridEditRowsStateSelector()
and using React DevTools to inspectapiRef.current.state
directly)I've made a very pared down version DataGridPro demo that has the basic row control functionality we need. It exhibits the same behavior described above: https://stackblitz.com/edit/react-8xnt9e?file=Demo.tsx
Specific steps to reproduce the issue
Due to the complexity of the row editing we have in our application (not evident in this simple demo), it's extremely important that the row state be responsive so the proper feedback can be given to our users.
Your environment
`npx @mui/envinfo`
Browsers used:
Search keywords: row editing, state selector, DataGridPro
The text was updated successfully, but these errors were encountered: