mui / mui-x

MUI X: Build complex and data-rich applications using a growing list of advanced React components, like the Data Grid, Date and Time Pickers, Charts, and more!
https://mui.com/x/
4.02k stars 1.24k forks source link

[question] Get updated row data after `setEditCellMode()` exits out #3654

Open danra2 opened 2 years ago

danra2 commented 2 years ago

Order ID 💳

32640

Duplicates

Latest version

Summary 💡

Having a difficult time finding a straightforward answer in the docs or stackoverflow. I edited a cell, and then I want to update the original data state following cell edit mode exiting out. It updates on the view, but it doesn't update the original data state, and it still sends the older data (I understand the concept of setState and referencing the new state after editing, I just don't know how to obtain the row object values in order to setState with). This is problematic for all things including deleting, submitting all, searching etc.

Is there an API prop method of doing this? Most of the options I've found online have me writing the logic manually (which I'm fine with, I just wanted to know if there is a API method that enables me to retrieve row object values once setCellEditMode() exits out).

I've figured out ways to get around this for submit all, but instead of finding a workaround I just need the official way of doing this. Basically I create a new array using the apiRef.Current.getSelectedRows() which gives me the updated cell values, and then I create a new array and iteratively push each object, finally I set the value. There has to be a more straightforwards way of doing this.

function SubmitSelectedOrders() { var dataArray = apiRef.current.getSelectedRows() var a = []; dataArray.forEach(element => a.push(element)); setData(dataArray); }

How do I access the the changed row values so that I can replace the old state? I believe the logical approach would be to have two arrays, compare ID's, if ID the corresponding ID is the same, then replace the existing object with the newly updated object, else continue along the array. However, I don't know what the built in method for calling the rows are. apiRef.Current.getSelectedRows() only works for selected rows, is there another way of getting the same after setCellEditMode() exits out? Or the current state of data following the edit?

Controlled editing The editRowsModel prop lets you control the editing state. You can handle the onEditRowsModelChange callback to control the GridEditRowsModel state. https://codesandbox.io/s/cerbp?file=/demo.js ^I've looked into this and got it to work, but it still doesn't update. SetEditRows to the newly updated model, doesn't work as it still shows the old data and doesn't reflect the new. It only shows the update on the GUI, but when I search it doesn't pick up the updated cell row data, but searches the initial setData().

Is there better documentation on how to use setEditCellProps()? https://codesandbox.io/s/mutable-brook-wkz27?file=/src/demo.tsx:3332-3337

The problem in depth 🔍

  // Method for deleting orders in batches.
  function DeleteBulkOrders(selectedRows){
    const selectedIDs = new Set(selectedRows);
    const selectedRowUpdate = data.filter((row) =>
    selectedIDs.has(row.id),
  )
    console.log(selectedRowUpdate)
    var deletedData = data.filter(function(id){
      return !selectedRowUpdate.includes(id);
    });
    setData(deletedData);
    handleClose();
  }

I have something along the lines of this for the MUI datagrid pro. Essentially when I selected a row via onSelected it spits me back out the ID of the selected items in an array. I then parse and filter that array of IDs in the original Data and set the new data.

This works, unfortunately, I believe the data that I'm using is failing to update upon cell change / edit. It still shows the old values and doesn't reflect the edited ones. This is all good for the delete batches method since I'm deleting them anyways, however it doesn't work so well for the submit all which needs to reflect the edited sells to send the payload to the API for storing.

  // Deletes individual items within the row, not selected / bulk item delete.
  function DeleteIndividualItem(params){
    console.log(params);
    const newArray = data
    .filter(function(row) {
      return row.id !== params.id;
    });
    setData(newArray);
  }

I also have individual delete, which actually sends in the params and accurately reflects the changes made to the cell and still deletes it.

I'm guessing that this has to do with State, and how I'm not updating my data state upon change. It took my awhile, but I found this built in method apiRef.current.getSelectedRows() ^This actually spits out the mapped array with the updated fields, I'm trying to see if there is a build in method within the MUI datatable that updates the rows by passing in apiRef.current.getSelectedRows().

I thought about destructing the map and then using setData(api.Ref.current.getSelectedRows()) etc. However, I feel like there should be a cleaner way of doing this.

var [data, setData] = useState(apiDataArray);

// Rows is the data array that is populated and fed back into the table.
var apiDataArray = [...Array(25)].map((_, index) => ({
  id: mockData.id(index),
  client: mockData.name.fullName(index),
  email: mockData.email(index),
  lastLogin: mockData.time(index),
  performance: mockData.number.percent(index),
  status: sample(['Accepted', 'Pending', 'Declined']),
}));

Context 🔦

CRUD operations on CustomDatagrid pro.

Your environment 🌎

System: OS: macOS Mojave 10.14.6 Binaries: Node: 16.9.1 - /usr/local/bin/node Yarn: 1.22.17 - /usr/local/bin/yarn npm: 7.21.1 - /usr/local/bin/npm Browsers: Chrome: 97.0.4692.71 Edge: Not Found Firefox: Not Found Safari: 12.1.2 npmPackages: @emotion/react: ^11.4.1 => 11.7.0 @emotion/styled: ^11.3.0 => 11.3.0 @mui/base: 5.0.0-alpha.57 @mui/core: 5.0.0-alpha.47 @mui/icons-material: ^5.0.0 => 5.0.0 @mui/lab: ^5.0.0-alpha.47 => 5.0.0-alpha.47 @mui/material: ^5.0.0 => 5.2.1 @mui/private-theming: 5.2.1 @mui/styled-engine: 5.2.0 @mui/styles: ^5.0.0 => 5.0.0 @mui/system: 5.2.1 @mui/types: 7.1.0 @mui/utils: ^5.0.0 => 5.2.1 @mui/x-data-grid: ^5.0.0-beta.1 => 5.0.0-beta.1 @mui/x-data-grid-pro: ^5.0.1 => 5.0.1 @mui/x-license-pro: 5.0.1 @types/react: 17.0.14 react: ^17.0.2 => 17.0.2 react-dom: ^17.0.2 => 17.0.2 typescript: ^4.3.5 => 4.3.5

DanailH commented 2 years ago

Hi, @danra2 thanks for raising this!

I'm not sure if understand the problem correctly, but the grid commits the updated cell value when the editing of the cell is over, so out of edit mode. There is a prop you can use to respond to that, onRowEditCommit that gets you the updated cell value. On another hand, there is the apiRef.current.getEditRowsModel that gets you all rows in edit mode. you can then find the row you are interested in and get its latest value. You can check an example using this here https://mui.com/components/data-grid/editing/#server-side-persistence-2. I apologize if this is not the problem you are having.

I'm tagging @m4theushw as he has more knowledge of how the editing works.

m4theushw commented 2 years ago

I also found difficult to understand what you're trying to do. It seems to me that you're calling apiRef.current.getSelectedRows to get the cell that was updated, which is not very correct. Did you take a look in https://mui.com/components/data-grid/editing/#server-side-persistence and https://mui.com/components/data-grid/editing/#server-side-persistence-2?

I would advise not to use the editRowsModel prop because it doesn't play with singleSelect and validation.

Is there better documentation on how to use setEditCellProps()?

This method is not in the API. Do you mean setEditCellValue?

oliviertassinari commented 2 years ago

Since the issue is missing key information, and has been inactive for 7 days, it has been automatically closed. If you wish to see the issue reopened, please provide the missing information.

jessejamesrich commented 2 years ago

tldr: My workaround was onCellFocusOut

I came across this issue as well, and I found their original question easy to understand since I was having an identical problem. Here's what I came up with:

DataGridPro was pretty straightforward until I got to this feature. The expected behaviour seemed self-evident so after dicking around with it for like 3 hours I ended up here. My take is that there's a bug, which seems unlikely, there's a deficiency, which seems probable, my code is crap, also possible, or there's a mismatch in my expectations and behaviour.

Here's how I'm getting stuck in a similar way. When editing with the param:

onCellEditStop={(param,event)=>{console.log(param);})

Neither param.value or param.formattedValue update when hitting enter or selecting out of cell. You would expect either of those values to update immediately. That being said, if you update a second time, the value from the previous edit will now update. It's one update behind.

If I move to onRowEditStop and then look for params.row.name, similarly the value doesn't update until the second go.

I even tried putting a timeout on this, knowing that'd be futile.

Now, reading this, for whatever reason, I missed in the documentation the event handle mentioned above. onCellEditCommit

I understand how I missed it. On the Data Grid > Editing page in your docs, it only lists four events. onCellEditStart, onCellEditStop, onRowEditStart, and onRowEditStop. This is where I got stuck. I assume this is where OP got stuck. Perhaps that docs page could be updated. So I got this far, now...

If I add onCellEditCommit or onRowEditCommit as a jsx param to <DataGridPro ... /> neither fire at all. No errors, warning, etc. Just nothing.

So, this has me believing that:

  1. The documentation is a little light,
  2. The examples by extension, are also a little light, and
  3. Some unknown issue is troubling onCellEditCommit and onRowEditCommit, or
  4. I've misunderstood onCellEditCommit and onRowEditCommit, and
  5. There's a bug in DataGrid* in which it only updates on the second round, or
  6. The expected behaviour is wrong (value, formattedValue, or name should update immediately), and/or
  7. API function should be called in place, and/or
  8. There's a problem in my own code, but...

While writing this I figured, perhaps I'm missing another, unrelated event handler, since the documentation feels incomplete. And I found onCellFocusOut which, wouldn't you know it, does update param.value and param.formattedValue value upon any blur event.

So, there's a lot of possibilities here, but the takeaway for me is that of the 4 event handles provided, none of them worked, and the one not related worked without issue.

I'm adding this in the event you find yourself supporting a similar issue, and for any folks out there that got stumped like I did.

onCellFocusOut was the workaround.

m4theushw commented 2 years ago

@jessejamesrich We released a new editing API with a processRowUpdate prop dedicated exclusively to get the data after changing the mode. Using this API you don't need to use onCellEditStop. You can check the docs in https://mui.com/components/data-grid/editing. Note that onCellEditCommit belongs to the legacy editing API that will be removed in v6. We recommend to upgrade to this new API to reduce the migration to the next major version. If you encounter any bug or need help to migrate to the new API feel free to open a new issue.

danra2 commented 2 years ago

@m4theushw So I'm working with that new API, but the default behavior of pressing enter to go into edit mode for the cell, and then pressing enter again to exit out of edit mode behavior seems to have been removed? How do I renable this behavior because my clients rely heavily on key shortcuts?

KJoyner commented 6 months ago

I was also looking for a way to get notified after a row has been updated. Not sure if the onRowEditCommit performed this but it looks deprecated in V6. The processRowUpdate is close but it returns before the actual model has been updated.

My use case is that I want to update other fields in a form shared with datagrid. To do this, I need to access the rows within datagrid anytime a row has been edited. So looking for something that would say row-has-changed after edit has been committed. Is there perhaps another way to get this functionality?

radmila-bord commented 3 weeks ago

I was having the same issue: update seemed like it was lagging one step behind. But then I realized that the function that does update of the cell - handleValueChange - is async. So it's being executed before the new value is available. The same function accepts "newValue" parameter. I did not see it in the example, but rather when looking at the underlying code for the "GridEditSingleSelectCell". Once I captured newValue parameter, I was able to update cell on the UI immediately.

Sample code:

function CustomTypeEditComponent(props: GridEditSingleSelectCellProps) {
  const apiRef = useGridApiContext()

  const handleValueChange = async (newValue: any) => {
  //my custom code to get email address based on the newValue
    apiRef.current.setEditCellValue({
      id: props.id,
      field: 'email',
      value: email
    })
  }
  return <GridEditSingleSelectCell onValueChange={handleValueChange} {...props} />
}

add this to your column definition: renderEditCell: (params: any) => ( <CustomTypeEditComponent {...params} /> )