Mui-datatables: How to update/delete row(s) with 'customBodyRender'

Created on 1 Dec 2018  路  14Comments  路  Source: gregnb/mui-datatables


I would like to enable Delete/Update of column with buttons I defined in 'customBodyRender'

Expected Behavior


mui-datatables support customized components inside table cells, I like that. I have made it to put some buttons in one column of the table, so user can interact with each row's data.
For each row, I have some columns with data, and one columns with 'delete' and 'update' button, I need to enable the buttons to work.

For example, one row of table look like this:

Name | Age | Options

Alex | 30 |

Current Behavior

I cannot delete rows from customized component with 'customBodyRender'

Steps to Reproduce (for bugs)

  1. First I have created a component
  2. I have put this component in mui-datatable with customBodyRender, it works fine.
    {
    name: "Options",
    options: {
    customBodyRender: (value, tableMeta, updateValue) => {
    return (
    value={value}
    tableMeta={tableMeta}
    change={event => updateValue(event)}
    />
    );
    },
    filter: false,
    sort: false
    },
  3. I know how to change the value of the current cell, from the example provided, I can also use the value of that cell to make some backend requests. but what I can do to:
    Delete the row in table from where I clicked the 'delete' button in my
  4. I suppose there should be some methods that I can pass to

Your Environment

| Tech | Version |
|--------------|---------|
| Material-UI | latest |
| MUI-datatables | latest |
| React | latest |
| browser | chrome |
| etc | |

@gregnb

question

All 14 comments

Finally I figure out a way to change the Table's data with buttons in 'customBodyRender'.
My original question is essentially asking: let's say we have a parent component and a child component , how can I update the data of from .
Because the relation of and is mysterious to me, although I spent some time to look at the source code, I cannot figure it out. Since there is not a 'setter' function provided in 'customBodyRender', I use React Redux to manage the table data.

  1. In my parent component, I need to fetch data with a Action method call 'fetchData'. when component load, then save it to Redux Store.
    `componentDidMount() {
    //cache the reference to this
    let current = this;
axios.get(URL)
  .then(function (response) {
    let data = response.data;
    if(data){
      current.props.fetchData(data); // Action to fetch data and save it to redux store
    }
  })
  .catch(function (error) {
    console.log(error);
  });

}`

Then I will load the data from Redux store.
function mapStateToProps(state) { return { data: state.data } }
Then feed it to MUI datatable:
<MUIDataTable title={"List"} data={this.props.data} columns={columns} options={options} />

  1. Then in my child component , I need to enable my buttons to update the Redux store.
    Let's look at one example of deleting the current row. I created an Action method called deleteData(id, list), it will remove the row of data with specified ID, then return the rest array list to Redux store.
    I have a button:
    <Button onClick={this.handleDelete} color="primary" autoFocus> Delete </Button>
    It will call a handleDelete method, in the method first we call a backend API to remove data, if success, then we will call the Action method 'deleteData to remove the same data from Redux store:
    ` handleDelete = () => {
    var current = this;
    var id = this.props.value;
    var {deleteData, data_list} = this.props;
    // deleteData is a Action Function, data_list is the Redux store data, both of them all passed to component with connect(mapStateToProps, mapDispatchToProps)(CustomButtons)

    var delete_url = ${DeleteURL}/${id};
    axios.delete(delete_url)
    .then(function(res) {
    deleteData(id, data_list); // update Redux store
    })
    .catch(function (error) {
    console.log(error);
    });
    }`

  2. Because our parent component is connect with Redux store, when we update the table data in our child component, the change of state update the parent component. And you see the row is deleted in the front end web page. Bingo!

Hey @zhouyuhang ! This is exactly the problem I'm facing with my current MUI-datatable build. I'm trying to add an edit and delete button as well utilizing the custom body render, I'm very close to getting right, but can't seem to refactor properly using I'm utilizing react and redux for state management. Really, I'm struggling with making button links to edit forms for each row, which would require maintaining the id for each row.

Would you be willing to assist or share your code? Thanks!

Same here... I can't achieve to make a delete request to my API when pressing the default Delete Icon (which triggers the onRowsDelete built in method. I don't know how to pass that row's ID to the function.(currently I have data from my db displayed con the table, so I need to pass to the onRowsDelete the id from my db). Do you know how to handle this? Thanks in advance

@nultron Were you able to figure this out? I was able to figure out a working solution and could help you if need be. There were some interesting configurations that I had to do, but for the most part the package wasn't as opinionated as I initially thought. Let me know!

yekijnuy Can you help me with the configuration you did?

@marianalucut7 Sure - first what are you trying to render? Send me some details and I'll be more than happy to help if you are still stuck

If you are trying to find the row id , everything you need is located in the tableMeta.
Try this inside your customBodyRender function
const rowId = tableMeta.rowData
console.log(rowId)

I had mine working the other day to render a edit icon but now for some reason it is not working.
Please let me know if you see a problem with this code.
`const columns = [
{
name: "ID",
options: {
display: false,
}
},
"Client Name",
"Date",
"Type",
"Memo",
"Status",
"Reatainer Rec",
{
name: "Edit",
options: {
display: true,
onCellClick: (rowIndex) => {this.handleEdit(rowIndex)},
customBodyRender: (value, tableMeta, updateValue) => {
const rowId = tableMeta.rowData
console.log(rowId)
// tableMeta contains all the info that is needed
return (

            <Icon name="edit" size="large" color="green" onClick={ e => this.handleEdit(rowId, e)}> 
            </Icon>
        );
      },         
    }
  }
];

`

How can we update the changed data to the database without using redux in the custom body render?

Hello,

I added my action buttons using customBodyRender:

{
           name: "Action",
                options: {
                    filter: true,
                    sort: false,
                    empty: true,
                    customBodyRender: () => {
                        return (
                            <ActionButtons
                                onView={this.onView}
                                onEdit={this.onEdit}
                                onDelete={this.onDelete}
                            />
                        );
                    }
                }
            },

And in table options, I use onRowClick to get the data (included an object of the component ) from the row clicked:

const options = {
            filter: true,
            download: false,
            selectableRows: false,
            filterType: "dropdown",
            responsive: "scroll",
            rowsPerPage: 10,
            customToolbar: () => (
                <CustomToolbar
                    handleFormOpen={this.handleFormOpen}
                />
            ),
            onRowClick: (event, rowData) => {
               this.handleClickedActionButton(event, rowData);
            },
        };

As I have 3 buttons in one cell (view, edit and delete) for each row, I need to know which button was clicked in order to perform an action to the data (call a function).

I tried using event.currentTarget to get the id of the button clicked, use that id in a switch statement in order to identify which one was and base on that call the correct function.

Something like this:

            onRowClick: (event, rowData) => {
               this.handleClickedActionButton(event, rowData);
            },
        };
//
handleClickedActionButton = (event, rowData ) => {
        const buttonID = event.currentTarget.dataset.id;
        switch (buttonID) {
            case "btnView":
                console.log("View");
                break;
            case "btnEdit":
                console.log("Edit");
                break;
            case "btnDelete":
                console.log("Delete");
                break;
            default: console.log("No action button clicked.");
            }
        };

But I just get an error saying "Type error: event.currentTarget is undefined".

How can I implement this idea in a better way? T.T

Hi, i read all comments and i had similar issue in my project i solve it using React hooks, sharing state and actions using a local context, here my example, i hope helping any;

context.js

import React from "react";

const TableContext = React.createContext({
  actions: {},
  stateTable: {},
  filterOptions: {}
});

export function useTableContext() {
  return React.useContext(TableContext);
}

export default TableContext;

index.js

import TableContext from './context';

function EnrolledContainer() {
  const classes = useStyles();
  const { actions, stateTable, filterOptions, handleClose, handleAddButton } = useEnrolledTable();
  const { title, isLoading, open, tableData } = stateTable;

  return (<TableContext.Provider value={{ actions, stateTable, filterOptions }}>
    <Grid container spacing={8} className={classes.container}>
      <Grid item xs={12} sm={12}>
        <MUIDataTable
          title={
            <React.Fragment>
              <span>
                <span style={{ fontSize: "1.5em", fontWeight: 'bolder', textAlign: 'center' }} >{title}</span>
              </span>
              <span>
                {!!isLoading && <CircularProgress size={24} thickness={6} style={{ marginLeft: 15, position: 'relative', top: 4, textAlign: 'center' }} />}
              </span>
            </React.Fragment>
          }
          data={tableData}
          columns={getColumns(stateTable, actions, filterOptions)}
          options={getOptions(stateTable, actions)}
        />
....

customBodyRender 馃憤

import { useTableContext } from '../context';

const ToggleButton = React.memo(props => {
  const value = props[0]
  const tableMeta = props[1]
  const updateValue = props[2]
  const { actions } = useTableContext();
  const { modificatePeople, getRowData, openAlertDialog, closeAlertDialog, updateRow, enqueueSnackbar, closeSnackbar } = actions
  const people = getRowData(tableMeta.rowIndex);

  return <FormControlLabel
    control={<Switch color="primary" checked={people && people.status} value={people && people.status ? "Activo" : "Inactivo"}
      onChange={event => {
        const value = event.target.checked;
        people && openAlertDialog({
          title: 'Cambiar estado ',
          message: "Est谩 seguro(a) de " + ((status) ? "bloquear" : "activar") + " a " + people.fname
        })
          .then((accept) => {
            if (accept) {
              closeAlertDialog();
              people.status = value;
              updateRow(tableMeta.rowIndex, people)
              people && modificatePeople({
                id: people.id,
                params: {
                  status: Number(value)
                }
              })
            }
          })
      }}
    />}
  />
})

export default (...args) => {
  return <ToggleButton {...args} />
}

Hi, I'm new to React and MUI-datatables. I have been creating a datatable where I am implementing CRUD. Everything else works perfectly apart from Update. I can only update the first row even when I click on the edit icon on other rows. Whenever I click on each edit icon in every row. A modal form pops up where I am supposed to update the details of a particular item (in this case, company). I am also using redux.

* Edit Clolumn*

{
            name: "Edit",
            options: {
              filter: false,
              sort: false,
              empty: true,
              customBodyRender: (value, tableMeta, updateValue) => {
                const rowId = tableMeta.rowData[0]
                return (
                    <div>
                      <EditIcon onClick={this.toggle} />
                       <Modal isOpen={this.state.modal} toggle={this.toggle}>
                       <ModalHeader toggle={this.toggle}>Update Company</ModalHeader>
                       <ModalBody>
                           <Form onSubmit={this.handleSubmit.bind(this, rowId)}>
                               <FormGroup>
                                   <Label for='name'>Name</Label>
                                   <Input
                                       type='text'
                                       name='name'
                                       id='item'
                                       placeholder='update name'
                                       onChange={this.handleChange}
                                   />
                                   <Label for='email'>Email</Label>
                                   <Input
                                       type='email'
                                       name='email'
                                       id='email'
                                       placeholder='update email'
                                       onChange={this.handleChange}
                                   />
                                   <Label for='phone'>Phone</Label>
                                   <Input
                                       type='text'
                                       name='phone'
                                       id='phone'
                                       placeholder='update phone'
                                       onChange={this.handleChange}
                                   />
                                   <Label for='website'>Website</Label>
                                   <Input
                                       type='url'
                                       name='website'
                                       id='website'
                                       placeholder='update website'
                                       onChange={this.handleChange}
                                   />
                                   <Label for='address'>Address</Label>
                                   <Input
                                       type='text'
                                       name='address'
                                       id='address'
                                       placeholder='update address'
                                       onChange={this.handleChange}
                                   />
                                   <Button color='dark' style={{ marginTop: '2rem' }} block>
                                       Update Company
                                   </Button>
                                </FormGroup>
                            </Form>
                        </ModalBody>
                       </Modal>
                   </div>
                );
              }
            }
          }, 

handleSubmit

handleSubmit = id => {  
      const company = {
        name: this.state.name,
        email: this.state.email,
        phone: this.state.phone,
        website: this.state.website,
        address: this.state.address
      };

      // update company via updateCompany action
      this.props.updateCompany(company, id);

      //Close modal
      this.toggle();

    };

What could I be doing wrong. Or is there a better way to do this?

Hi, I'm new to React and MUI-datatables. I have been creating a datatable where I am implementing CRUD. Everything else works perfectly apart from Update. I can only update the first row even when I click on the edit icon on other rows. Whenever I click on each edit icon in every row. A modal form pops up where I am supposed to update the details of a particular item (in this case, company). I am also using redux.

* Edit Clolumn*

{
            name: "Edit",
            options: {
              filter: false,
              sort: false,
              empty: true,
              customBodyRender: (value, tableMeta, updateValue) => {
                const rowId = tableMeta.rowData[0]
                return (
                    <div>
                      <EditIcon onClick={this.toggle} />
                       <Modal isOpen={this.state.modal} toggle={this.toggle}>
                       <ModalHeader toggle={this.toggle}>Update Company</ModalHeader>
                       <ModalBody>
                           <Form onSubmit={this.handleSubmit.bind(this, rowId)}>
                               <FormGroup>
                                   <Label for='name'>Name</Label>
                                   <Input
                                       type='text'
                                       name='name'
                                       id='item'
                                       placeholder='update name'
                                       onChange={this.handleChange}
                                   />
                                   <Label for='email'>Email</Label>
                                   <Input
                                       type='email'
                                       name='email'
                                       id='email'
                                       placeholder='update email'
                                       onChange={this.handleChange}
                                   />
                                   <Label for='phone'>Phone</Label>
                                   <Input
                                       type='text'
                                       name='phone'
                                       id='phone'
                                       placeholder='update phone'
                                       onChange={this.handleChange}
                                   />
                                   <Label for='website'>Website</Label>
                                   <Input
                                       type='url'
                                       name='website'
                                       id='website'
                                       placeholder='update website'
                                       onChange={this.handleChange}
                                   />
                                   <Label for='address'>Address</Label>
                                   <Input
                                       type='text'
                                       name='address'
                                       id='address'
                                       placeholder='update address'
                                       onChange={this.handleChange}
                                   />
                                   <Button color='dark' style={{ marginTop: '2rem' }} block>
                                       Update Company
                                   </Button>
                                </FormGroup>
                            </Form>
                        </ModalBody>
                       </Modal>
                   </div>
                );
              }
            }
          }, 

handleSubmit

handleSubmit = id => {  
      const company = {
        name: this.state.name,
        email: this.state.email,
        phone: this.state.phone,
        website: this.state.website,
        address: this.state.address
      };

      // update company via updateCompany action
      this.props.updateCompany(company, id);

      //Close modal
      this.toggle();

    };

What could I be doing wrong. Or is there a better way to do this?

You would need to make your id column in the table to have display:'excluded'

is very easy, check this.... it's ok for me:

  1. Add the event onRowsDelete on the options section:

    const options = {
    onRowsDelete: (rowData, rowState) => {
    handleDeleteCat(rowData, rowState);
    },
    filter: false,
    selectableRows: true,
    filterType: 'dropdown',
    responsive: 'stacked',
    rowsPerPage: 10,
    }

  2. Create a function for getting the event, and get the row data index. Finally, get from the object list the index position and get the id. With it you can delete the row in your database:

    const handleDeleteCat = (rowIndex, dataIndex) => {
    const rowToDelete = rowIndex.data[0].dataIndex;
    var idCatToDelete = props.state.productCategories.categoryList.categoriesList[rowToDelete][0];
    };

Was this page helpful?
0 / 5 - 0 ratings

Related issues

T-pirithiviraj picture T-pirithiviraj  路  3Comments

alexanderwhatley picture alexanderwhatley  路  4Comments

JordanKadish picture JordanKadish  路  4Comments

cahna picture cahna  路  3Comments

NickToye picture NickToye  路  4Comments