Creating a dynamic inline editable table in React — Part 1

Vanu Protap Verma
4 min readMar 27, 2020

In this post, I am going to share the steps of creating a dynamic table in React. We will start from creating a simple dynamic table generated from some sample data. Then we will go further and make the dynamic table to do some fancy stuff :) I hope you find this post interesting.

Note: To develop the code base for this post, I have created a new React app using create-react-app. I assume you know how to create a React app. I am wishing to keep the code very simple with minimal styling, here I am using bootstrap to do some simple styling.

Goal:

In this post, we will create a GenericCustomTable component, an Users component and then use these components to display a table generated dynamically.

Let’s start

We assume our sample Users data looks like below:

[ 
{
"id":1,
"first_name":"Shara",
"last_name":"Weeds",
"email":"sweeds0@barnesandnoble.com",
"gender":"Female"
},
{
"id":2,
"first_name":"Conant",
"last_name":"Puddan",
"email":"cpuddan1@ihg.com",
"gender":"Male"
},
{
"id":3,
"first_name":"Mehetabel",
"last_name":"Mawtus",
"email":"mmawtus2@sakura.ne.jp",
"gender":"Female"
}
]

and we wanted to display this data in a generic table. So let’s create a GenericCustomTable component first.

GenericCustomTable component (GenericCustomTable.js)

I am sharing the code for GenericCustomTable in one place to make this post smaller (function names are self explanatory).

import React from 'react';
import PropTypes from 'prop-types';
export default class GenericCustomTable extends React.Component {
constructor(props) {
super(props);
this.state = {
currentTableData: Object.assign([], this.props.data),
columnsToDisplay: Object.assign([], this.props.columns)
};
this.renderHeaders = this.renderHeaders.bind(this);
this.renderRows = this.renderRows.bind(this);
this.renderIndividualRow = this.renderIndividualRow.bind(this);
}
renderHeaders() {
return this.state.columnsToDisplay.map((item, index) => {
const headerCssClassName = `col-md-${item.columnSize}`;
if (item.visible) {
return (
<div className={headerCssClassName} key={index}>
<span className="table-column-header-text">
{item.displayText}
</span>
</div>
);
} else {
return (
<div className={headerCssClassName} key={index} hidden>
<span className="table-column-header-text">
{item.displayText}
</span>
</div>
);
}
});
}
renderIndividualRow(data, dataKeys) {
return dataKeys.map((item, index) => {
let columnWidth = `col-md-${this.state.columnsToDisplay[index].columnSize}`;
if (item.visible) {
return (
<div className={columnWidth} key={index}>
{data[item.fieldName]}
</div>
);
} else {
return null;
}
});
}
renderRows() {
let dataKeys = Object.assign([], this.state.columnsToDisplay);
let dataRows = Object.assign([], this.state.currentTableData);
if (dataRows.length > 0) {
return dataRows.map((row, index) => {
return (
<div key={index} className="row">
{this.renderIndividualRow(row, dataKeys)}
</div>
);
});
}
}
render() {
return (
<div className="col-md-12">
<div className="row column-header-row">
{this.renderHeaders()}
</div>
{this.renderRows()}
</div>
);
}
}
GenericCustomTable.propTypes = {
data: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.shape({
fieldName: PropTypes.string,
displayText: PropTypes.string,
visible: PropTypes.bool.isRequired,
columnSize: PropTypes.number.isRequired
})).isRequired
};

In props, data is an array of records to be rendered in the table and columns is an array of columns that we want to be displayed. This is important in case we do not want to show all data in the table (i.e. hide the id column). The fieldName in columns array will be used to get the corresponding field value from data array. Similarly, displayText will be used to populate column header text for our table.

Now that we have defined table component, let’s create our Users component where we will be using our generic table component to render the users data that we have defined at the beginning.

Users component (Users.js)

import React from 'react';
import GenericCustomTable from './GenericCustomTable';
export default class Users extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [
{
"id": 1,
"first_name": "Shara",
"last_name": "Weeds",
"email": "sweeds0@barnesandnoble.com",
"gender": "Female"
},
{
"id": 2,
"first_name": "Conant",
"last_name": "Puddan",
"email": "cpuddan1@ihg.com",
"gender": "Male"
},
{
"id": 3,
"first_name": "Mehetabel",
"last_name": "Mawtus",
"email": "mmawtus2@sakura.ne.jp",
"gender": "Female"
}
]
};
this.getDisplayColumns = this.getDisplayColumns.bind(this);
}
// the 'visible' property is used to show/hide any column
// visible fields 'columnSize' should sum up to 12 (bootstrap
// recommendation)
getDisplayColumns() {
return [
{
fieldName: 'id',
displayText: 'ID',
visible: false,
columnSize: 1,
},
{
fieldName: 'first_name',
displayText: 'First name',
visible: true,
columnSize: 2,
},
{
fieldName: 'last_name',
displayText: 'Last name',
visible: true,
columnSize: 2,
},
{
fieldName: 'email',
displayText: 'Email',
visible: true,
columnSize: 5,
},
{
fieldName: 'gender',
displayText: 'Gender',
visible: true,
columnSize: 2,
}
];
};
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-12">
Rendered Generic Custom Table
</div>
</div>
<br />
<div className="row">
<GenericCustomTable
data={this.state.users}
columns={this.getDisplayColumns()}
/>
</div>
</div>
);
}
}

One very important thing to note here that, to display data values for individual columns, the fieldName value must match the propertyName from the data.

Now that we have our Users component ready, let’s update the App.js file to use the newly created Users component.

App (App.js)

import React from 'react';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import logo from './logo.svg';
import './App.css';
import Users from './Users';function App() {
return (
<div className="App">
<Users />
</div>
);
}
export default App;

Let’s check what our changes look like as of now.

screenshot 1: visible property for ID column is “true”
screenshot 2: visible property for ID column is “false”

We have our dynamic table that is rendering only the columns we want :)

Great job and thank you for sticking with me :)

Links:

Part 2 of this series can be found here

Part 3 of this series is here

--

--