How to add a button inside a cell in the DevExtreme React Grid?

I'm currently working with react grid from DevExtreme and I've faced a similar problem but followed a different solution. What I did was create a new Plugin in order to add a new column called "ActionsColumn". You can pass both nodes and associated callbacks to the plugin. The minimum code would be something like this (sorry, not tested):

import React from 'react'
import PropTypes from 'prop-types'
import IconButton from '@material-ui/core/IconButton'
import { TABLE_HEADING_TYPE } from '@devexpress/dx-grid-core'
import {Getter, Template, Plugin} from '@devexpress/dx-react-core'
import {
    Table,
} from '@devexpress/dx-react-grid-material-ui'

const pluginDependencies = [
    {name: 'Table'},
];

const ACTIONS_COLUMN_TYPE = 'actionsColumnType';

function tableColumnsWithActions(tableColumns, width) {
    return [...tableColumns, {key: ACTIONS_COLUMN_TYPE, type: ACTIONS_COLUMN_TYPE, width: width}];
}

function isHeadingActionsTableCell(tableRow, tableColumn) {
    return tableRow.type === TABLE_HEADING_TYPE && tableColumn.type === ACTIONS_COLUMN_TYPE;
}

function isActionsTableCell(tableRow, tableColumn) {
    return tableRow.type !== TABLE_HEADING_TYPE && tableColumn.type === ACTIONS_COLUMN_TYPE;
}

export class ActionsColumn extends React.PureComponent {
    render() {
        const {
            actions,
            width,
        } = this.props;
        const tableColumnsComputed = ({tableColumns}) => tableColumnsWithActions(tableColumns, width);

        return (
            <Plugin
                name="ActionsColumn"
                dependencies={pluginDependencies}
            >
                <Getter name="tableColumns" computed={tableColumnsComputed}/>

                <Template
                    name="tableCell"
                    predicate={({tableRow, tableColumn}) =>
                    isHeadingActionsTableCell(tableRow, tableColumn)}
                >
                    <Table.Cell>Actions Column</Table.Cell>
                </Template>
                <Template
                    name="tableCell"
                    predicate={({tableRow, tableColumn}) => isActionsTableCell(tableRow, tableColumn)}
                >
                    {params => (
                        <Table.Cell {...params} row={params.tableRow.row}>
                            {actions.map(action => {
                                const id = params.tableRow.row.id;
                                return (
                                    <IconButton onClick={() => action.action(id)}>
                                        {action.icon}
                                    </IconButton>
                                )

                            })}
                       </Table.Cell>
                    )}
                </Template>
            </Plugin>
        );
    }
}
ActionsColumn.propTypes = {
    actions: PropTypes.arrayOf(PropTypes.PropTypes.shape({
        icon: PropTypes.node,
        action: PropTypes.func.isRequired
    })).isRequired,
    width: PropTypes.number
};
ActionsColumn.defaultProps = {
    width: 240,
};

You simply check whether you are in a header row or a regular table row and you just add a header or the actions you defined respectively.

In order to use this plugin, simply include it in your Grid definition after the Table plugin declaration:

render() {
    const {columns, rows} = this.state;
    const actions = [
        {
            icon: <DeleteIcon/>,
            action: doAlert
        },
        {
            icon: <EditIcon/>,
            action: id => alert('edit id: ' + id)
        }
    ];

    return (
        <Grid rows={rows} columns={columns} getRowId={getRowId}>
            <Table/>
            <TableHeaderRow/>
            <ActionsColumn actions={actions}/>
        </Grid>
    )
}

The way I came up with this solution is quite straightforward:

  1. Read "DevExtreme React Grid - Blog Series" by Oliver Sturm
  2. Read React Core documentation.
  3. Check the existing table-edit-column source code at GitHub: here and here

Hope this helps.


I have forked your sandbox code and did some additions to it. Hope that's what you are looking for. And below is the same code. Hope this helps.

import React from "react";
import { render } from "react-dom";
import Paper from "@material-ui/core/Paper";
import // State or Local Processing Plugins
"@devexpress/dx-react-grid";
import {
    Grid,
    Table,
    TableHeaderRow
} from "@devexpress/dx-react-grid-material-ui";

class App extends React.PureComponent {
    constructor(props) {
        super(props);

        this.state = {
            columns: [
                { name: "name", title: "Name" },
                { name: "sex", title: "Sex" },
                { name: "city", title: "City" },
                { name: "car", title: "Car" },
                { name: "action", title: "action" }
            ],
            rows: [
                {
                    sex: "Female",
                    name: "Sandra",
                    city: "Las Vegas",
                    car: "Audi A4",
                    action: this.addResetBtn.call(this, { index: 0 })
                },
                { sex: "Male", name: "Paul", city: "Paris", car: "Nissan Altima" },
                { sex: "Male", name: "Mark", city: "Paris", car: "Honda Accord" },
                { sex: "Male", name: "Paul", city: "Paris", car: "Nissan Altima" },
                { sex: "Female", name: "Linda", city: "Austin", car: "Toyota Corolla" },
                {
                    sex: "Male",
                    name: "Robert",
                    city: "Las Vegas",
                    car: "Chevrolet Cruze",
                    action: this.addResetBtn.call(this, { index: 5 })
                },
                { sex: "Female", name: "Lisa", city: "London", car: "BMW 750" },
                { sex: "Male", name: "Mark", city: "Chicago", car: "Toyota Corolla" },
                {
                    sex: "Male",
                    name: "Thomas",
                    city: "Rio de Janeiro",
                    car: "Honda Accord"
                },
                { sex: "Male", name: "Robert", city: "Las Vegas", car: "Honda Civic" },
                { sex: "Female", name: "Betty", city: "Paris", car: "Honda Civic" },
                {
                    sex: "Male",
                    name: "Robert",
                    city: "Los Angeles",
                    car: "Honda Accord"
                },
                {
                    sex: "Male",
                    name: "William",
                    city: "Los Angeles",
                    car: "Honda Civic"
                },
                { sex: "Male", name: "Mark", city: "Austin", car: "Nissan Altima" }
            ]
        };
    }

    addResetBtn = ({ index }) => {
        return (
            <button
                className="btn"
                onClick={this.handleResetClick.bind(this, { index: index })}
            >
                Reset
            </button>
        );
    };

    handleResetClick = ({ index }) => {
        const updatedRows = [...this.state.rows];
        updatedRows[index].car = "";
        this.setState({ rows: updatedRows });
    };

    render() {
        const { rows, columns } = this.state;

        return (
            <Paper>
                <Grid rows={rows} columns={columns}>
                    <Table />
                    <TableHeaderRow />
                </Grid>
            </Paper>
        );
    }
}

render(<App />, document.getElementById("root"));