Skip to content
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] Full-featured CRUD not editing data retrieved from database #12382

Closed
myarasi8 opened this issue Mar 8, 2024 · 3 comments
Closed
Labels
component: data grid This is the name of the generic UI component, not the React module! feature: Editing Related to the data grid Editing feature support: question Community support but can be turned into an improvement

Comments

@myarasi8
Copy link

myarasi8 commented Mar 8, 2024

Steps to reproduce

Link to live example: (required)

Steps:

  1. Start with the code provided under Full-featured CRUD at this website https://mui.com/x/react-data-grid/editing/#full-featured-crud
  2. Replace the code in Demo.tsx with the code below. The major change here is that instead of hard coding the columns, the columns are are set in the useEffect upon rendering of the component. In my use case, I have a fetch request in the useEffect and I only know what the columns are once I've retrieved it and performed a setState. In the attached code I don't have the fetch request but simply set the hard coded columns in the useEffect.
import * as React from 'react';
import { useState, useEffect } from 'react'
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import AddIcon from '@mui/icons-material/Add';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/DeleteOutlined';
import SaveIcon from '@mui/icons-material/Save';
import CancelIcon from '@mui/icons-material/Close';
import {
  GridRowsProp,
  GridRowModesModel,
  GridRowModes,
  DataGrid,
  GridColDef,
  GridToolbarContainer,
  GridActionsCellItem,
  GridEventListener,
  GridRowId,
  GridRowModel,
  GridRowEditStopReasons,
} from '@mui/x-data-grid';
import {
  randomCreatedDate,
  randomTraderName,
  randomId,
  randomArrayItem,
} from '@mui/x-data-grid-generator';

const roles = ['Market', 'Finance', 'Development'];
const randomRole = () => {
  return randomArrayItem(roles);
};

const initialRows: GridRowsProp = [
  {
    id: randomId(),
    name: randomTraderName(),
    age: 25,
    joinDate: randomCreatedDate(),
    role: randomRole(),
  },
  {
    id: randomId(),
    name: randomTraderName(),
    age: 36,
    joinDate: randomCreatedDate(),
    role: randomRole(),
  },
  {
    id: randomId(),
    name: randomTraderName(),
    age: 19,
    joinDate: randomCreatedDate(),
    role: randomRole(),
  },
  {
    id: randomId(),
    name: randomTraderName(),
    age: 28,
    joinDate: randomCreatedDate(),
    role: randomRole(),
  },
  {
    id: randomId(),
    name: randomTraderName(),
    age: 23,
    joinDate: randomCreatedDate(),
    role: randomRole(),
  },
];

interface EditToolbarProps {
  setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void;
  setRowModesModel: (
    newModel: (oldModel: GridRowModesModel) => GridRowModesModel,
  ) => void;
}

function EditToolbar(props: EditToolbarProps) {
  const { setRows, setRowModesModel } = props;

  const handleClick = () => {
    const id = randomId();
    setRows((oldRows) => [...oldRows, { id, name: '', age: '', isNew: true }]);
    setRowModesModel((oldModel) => ({
      ...oldModel,
      [id]: { mode: GridRowModes.Edit, fieldToFocus: 'name' },
    }));
  };

  return (
    <GridToolbarContainer>
      <Button color="primary" startIcon={<AddIcon />} onClick={handleClick}>
        Add record
      </Button>
    </GridToolbarContainer>
  );
}

export default function FullFeaturedCrudGrid() {
  const [rows, setRows] = React.useState(initialRows);
  const [columns, setColumns] = React.useState<GridColDef[]>([]);
  const [rowModesModel, setRowModesModel] = React.useState<GridRowModesModel>({});

  useEffect(() => {
    const temp_columns: GridColDef[] = [
        { field: 'name', headerName: 'Name', width: 180, editable: true },
        {
          field: 'age',
          headerName: 'Age',
          type: 'number',
          width: 80,
          align: 'left',
          headerAlign: 'left',
          editable: true,
        },
        {
          field: 'joinDate',
          headerName: 'Join date',
          type: 'date',
          width: 180,
          editable: true,
        },
        {
          field: 'role',
          headerName: 'Department',
          width: 220,
          editable: true,
          type: 'singleSelect',
          valueOptions: ['Market', 'Finance', 'Development'],
        },
        {
          field: 'actions',
          type: 'actions',
          headerName: 'Actions',
          width: 100,
          cellClassName: 'actions',
          getActions: ({ id }) => {
            console.log("id: " + id + " rowModesModel: " + JSON.stringify(rowModesModel))
            console.log("mode: " + rowModesModel[id]?.mode)
            const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit;
    
            if (isInEditMode) {
              return [
                <GridActionsCellItem
                  icon={<SaveIcon />}
                  label="Save"
                  sx={{
                    color: 'primary.main',
                  }}
                  onClick={handleSaveClick(id)}
                />,
                <GridActionsCellItem
                  icon={<CancelIcon />}
                  label="Cancel"
                  className="textPrimary"
                  onClick={handleCancelClick(id)}
                  color="inherit"
                />,
              ];
            }
    
            return [
              <GridActionsCellItem
                icon={<EditIcon />}
                label="Edit"
                className="textPrimary"
                onClick={handleEditClick(id)}
                color="inherit"
              />,
              <GridActionsCellItem
                icon={<DeleteIcon />}
                label="Delete"
                onClick={handleDeleteClick(id)}
                color="inherit"
              />,
            ];
          },
        },
      ];
      setColumns(temp_columns);
  }, [])

  const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => {
    if (params.reason === GridRowEditStopReasons.rowFocusOut) {
      event.defaultMuiPrevented = true;
    }
  };

  const handleEditClick = (id: GridRowId) => () => {
    setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } });
  };

  const handleSaveClick = (id: GridRowId) => () => {
    setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } });
  };

  const handleDeleteClick = (id: GridRowId) => () => {
    setRows(rows.filter((row) => row.id !== id));
  };

  const handleCancelClick = (id: GridRowId) => () => {
    setRowModesModel({
      ...rowModesModel,
      [id]: { mode: GridRowModes.View, ignoreModifications: true },
    });

    const editedRow = rows.find((row) => row.id === id);
    if (editedRow!.isNew) {
      setRows(rows.filter((row) => row.id !== id));
    }
  };

  const processRowUpdate = (newRow: GridRowModel) => {
    const updatedRow = { ...newRow, isNew: false };
    setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row)));
    return updatedRow;
  };

  const handleRowModesModelChange = (newRowModesModel: GridRowModesModel) => {
    setRowModesModel(newRowModesModel);
  };

//   const columns: GridColDef[] = [
//     { field: 'name', headerName: 'Name', width: 180, editable: true },
//     {
//       field: 'age',
//       headerName: 'Age',
//       type: 'number',
//       width: 80,
//       align: 'left',
//       headerAlign: 'left',
//       editable: true,
//     },
//     {
//       field: 'joinDate',
//       headerName: 'Join date',
//       type: 'date',
//       width: 180,
//       editable: true,
//     },
//     {
//       field: 'role',
//       headerName: 'Department',
//       width: 220,
//       editable: true,
//       type: 'singleSelect',
//       valueOptions: ['Market', 'Finance', 'Development'],
//     },
//     {
//       field: 'actions',
//       type: 'actions',
//       headerName: 'Actions',
//       width: 100,
//       cellClassName: 'actions',
//       getActions: ({ id }) => {
//         console.log("id: " + id + " rowModesModel: " + JSON.stringify(rowModesModel))
//         console.log("mode: " + rowModesModel[id]?.mode)
//         const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit;

//         if (isInEditMode) {
//           return [
//             <GridActionsCellItem
//               icon={<SaveIcon />}
//               label="Save"
//               sx={{
//                 color: 'primary.main',
//               }}
//               onClick={handleSaveClick(id)}
//             />,
//             <GridActionsCellItem
//               icon={<CancelIcon />}
//               label="Cancel"
//               className="textPrimary"
//               onClick={handleCancelClick(id)}
//               color="inherit"
//             />,
//           ];
//         }

//         return [
//           <GridActionsCellItem
//             icon={<EditIcon />}
//             label="Edit"
//             className="textPrimary"
//             onClick={handleEditClick(id)}
//             color="inherit"
//           />,
//           <GridActionsCellItem
//             icon={<DeleteIcon />}
//             label="Delete"
//             onClick={handleDeleteClick(id)}
//             color="inherit"
//           />,
//         ];
//       },
//     },
//   ];

  return (
    <Box
      sx={{
        height: 500,
        width: '100%',
        '& .actions': {
          color: 'text.secondary',
        },
        '& .textPrimary': {
          color: 'text.primary',
        },
      }}
    >
      <DataGrid
        rows={rows}
        columns={columns}
        editMode="row"
        rowModesModel={rowModesModel}
        onRowModesModelChange={handleRowModesModelChange}
        onRowEditStop={handleRowEditStop}
        processRowUpdate={processRowUpdate}
        slots={{
          toolbar: EditToolbar,
        }}
        slotProps={{
          toolbar: { setRows, setRowModesModel },
        }}
      />
    </Box>
  );
}

Current behavior

When I hit the edit button, the row looks like it is editable but the row isn't actually in edit mode, so the save and delete icons don't show up.
image

As you can see, DataGrid does display the rows and columns correctly, it just doesn't understand that the row is in edit mode now.

Expected behavior

Once the edit button is hit, the Save and Delete icons should replace the Edit and Delete icons.

Context

The situation is that I don't know what the rows and columns are going to be until I retrieve those values from the database. What I expect is for fetch request to get those values and then perform a setState setting the rows and columns which invokes a re-render of the DOM (this part works btw). But then, the edit functionality should still work as if the rows and columns were hard coded or passed in as props.

Your environment

npx @mui/envinfo
  Don't forget to mention which browser you used.
  Output from `npx @mui/envinfo` goes here.

These are the versions of mui I am using:
image

I am using Google Chrome for the browser.

Search keywords: React, Full-featured CRUD

@myarasi8 myarasi8 added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Mar 8, 2024
@myarasi8 myarasi8 changed the title Full-featured CRUD only editing hard-coded data Full-featured CRUD not editing data retrieved from database. Only editing hard coded data Mar 8, 2024
@michelengelen
Copy link
Member

Hey @myarasi8 the solution for this is simple: Since you are depending on rowModesModel in your useEffect you need to add it to the dependencies-array

    ];
    setColumns(temp_columns);
- }, []);
+ }, [rowModesModel]);

When you add this change it works as expected.

Please feel free to close the issue when this works for you!
Thanks! 🙇🏼

@michelengelen michelengelen added support: question Community support but can be turned into an improvement component: data grid This is the name of the generic UI component, not the React module! status: waiting for author Issue with insufficient information feature: Editing Related to the data grid Editing feature and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Mar 8, 2024
@michelengelen michelengelen changed the title Full-featured CRUD not editing data retrieved from database. Only editing hard coded data [data grid] Full-featured CRUD not editing data retrieved from database Mar 8, 2024
@myarasi8
Copy link
Author

myarasi8 commented Mar 8, 2024

Ah yes, thank you!

@myarasi8 myarasi8 closed this as completed Mar 8, 2024
Copy link

github-actions bot commented Mar 8, 2024

⚠️ This issue has been closed.
If you have a similar problem, please open a new issue and provide details about your specific problem.
If you can provide additional information related to this topic that could help future readers, please feel free to leave a comment.

How did we do @myarasi8?
Your experience with our support team matters to us. If you have a moment, please share your thoughts through our brief survey.

@github-actions github-actions bot added status: waiting for maintainer These issues haven't been looked at yet by a maintainer and removed status: waiting for author Issue with insufficient information labels Mar 8, 2024
@michelengelen michelengelen removed the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Mar 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: data grid This is the name of the generic UI component, not the React module! feature: Editing Related to the data grid Editing feature support: question Community support but can be turned into an improvement
Projects
None yet
Development

No branches or pull requests

2 participants