Generating a table based on an array of objects I have a React component that generates a table based on an array of objects, which gets populated with data in an object.

I'm trying to learn as much as I can about React.js / JavaScript. Could anyone with more experience in this area show me a way to simplify the code that I have here? Or if there are any other 'fancy' ways of doing what is being done in the code?

App.js

import React, { Component } from 'react';
import Table from './Table';

//Columns defines table headings and properties to be placed into the body
let columns = [
  {
    heading: 'Name',
    property: 'name'
  },
  {
    heading: 'Age',
    property: 'age'
  },
  {
    heading: 'Sex',
    property: 'sex'
  },
  {
    heading: 'Breed',
    property: 'breed'
  },
]

//Data is the array of objects to be placed into the table
let data = [
  {
    name: 'Sabrina',
    age: '6',
    sex: 'Female',
    breed: 'Staffordshire'
  },
  {
    name: 'Max',
    age: '2',
    sex: 'Male',
    breed: 'Boxer'
  }
]

class App extends Component {

  render() {
    return (
      &lt;&gt;
        &lt;Table
          columns={columns}
          data={data}
          propertyAsKey='name' //The data property to be used as a key
        /&gt;
      &lt;/&gt;
    );
  }
}

export default App;


Table.js

import React, { Component } from 'react';

class Table extends Component {

  buildTable = (columns, data, key) =&gt; {
    let headerRow = [];
    let dataRows = [];

    //Build the table header
    columns.forEach (col =&gt; {
      headerRow.push(
        &lt;th key={`header-${col.heading}`}&gt;{col.heading}&lt;/th&gt;
      );
    });

    //Build the rows
    data.forEach(item =&gt; {
      let dataCells = [];

      //Build cells for this row
      columns.forEach (col =&gt; {
        dataCells.push(
          &lt;td key={`${item[key]}-${col.property}`}&gt;{item[col.property]}&lt;/td&gt;
        );
      });

      //Push out row
      dataRows.push(
        &lt;tr key={`${item[key]}-row`}&gt;{dataCells}&lt;/tr&gt;
      )
    });

    return(
      &lt;&gt;
        &lt;thead&gt;
          &lt;tr&gt;{headerRow}&lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
          {dataRows}
        &lt;/tbody&gt;
      &lt;/&gt;
    );
  };

  render() {
    const {
      columns,
      data,
      propertyAsKey
    } = this.props;

    return (
      &lt;table className='table'&gt;
        {this.buildTable(columns, data, propertyAsKey)}
      &lt;/table&gt;
    );
  }
}

export default Table;
TL;DR Fully functionnal example :



//Columns defines table headings and properties to be placed into the body
const columns = [{ heading: 'Name', property: 'name' }, { heading: 'Age', property: 'age' }, { heading: 'Sex', property: 'sex' }, { heading: 'Breed', property: 'breed' },]

//Data is the array of objects to be placed into the table
const data = [{ name: 'Sabrina', age: '6', sex: 'Female', breed: 'Staffordshire' }, { name: 'Max', age: '2', sex: 'Male', breed: 'Boxer' }]

const App = props =&gt; &lt;Table columns={columns} data={data} propertyAsKey='name' /&gt;

const Table = ({ columns, data, propertyAsKey }) =&gt; 
    &lt;table className='table'&gt;
        &lt;thead&gt;
            &lt;tr&gt;{columns.map(col =&gt; &lt;th key={`header-${col.heading}`}&gt;{col.heading}&lt;/th&gt;)}&lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
            {data.map(item =&gt;
                &lt;tr key={`${item[propertyAsKey]}-row`}&gt;
                    {columns.map(col =&gt; &lt;td key={`${item[propertyAsKey]}-${col.property}`}&gt;{item[col.property]}&lt;/td&gt;)}
                &lt;/tr&gt;
            )}
        &lt;/tbody&gt;
    &lt;/table&gt;
    
ReactDOM.render(&lt;App/&gt;, document.getElementById('root'))
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"&gt;&lt;/script&gt;
&lt;div id='root'&gt;






The main thing that would highly reduce this code is the map function. This function can be applied on an array, execute a callback on every item in the array and returns a new array.

By using it you can reduce the following code :

let headerRow = [];

columns.forEach(col =&gt; {
    headerRow.push(
        &lt;th key={`header-${col.heading}`}&gt;{col.heading}&lt;/th&gt;
    );
});


Can be reduced to this with the exact same result :

const headerRow = columns.map(col =&gt; &lt;th key={`header-${col.heading}`}&gt;{col.heading}&lt;/th&gt;)


You can now include this function directly into your JSX :

&lt;thead&gt;
    &lt;tr&gt;{columns.map(col =&gt; &lt;th key={`header-${col.heading}`}&gt;{col.heading}&lt;/th&gt;)}&lt;/tr&gt;
&lt;/thead&gt;


And the same thing with nested mapping for the body :

&lt;tbody&gt;
    {data.map(item =&gt; 
        &lt;tr key={`${item[key]}-row`}&gt;
            {columns.map(col =&gt; &lt;td key={`${item[key]}-${col.property}`}&gt;{item[col.property]}&lt;/td&gt;)}
        &lt;/tr&gt;
    )}
&lt;/tbody&gt;


Your full buildTable function is now reduced to the following :

buildTable = (columns, data, key) =&gt; {
    return (
        &lt;&gt;
            &lt;thead&gt;
                &lt;tr&gt;{columns.map(col =&gt; &lt;th key={`header-${col.heading}`}&gt;{col.heading}&lt;/th&gt;)}&lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;
                {data.map(item =&gt; 
                    &lt;tr key={`${item[key]}-row`}&gt;
                        {columns.map(col =&gt; &lt;td key={`${item[key]}-${col.property}`}&gt;{item[col.property]}&lt;/td&gt;)}
                    &lt;/tr&gt;
                )}
            &lt;/tbody&gt;
        &lt;/&gt;
    );
};


If you want to go a little further you can also delete this function and embed this code in your render function JSX. I will also convert your component into a stateless function, since you are not using any state value :

const Table = ({ columns, data, propertyAsKey }) =&gt; //Deconstructs your props
    &lt;table className='table'&gt;
        &lt;thead&gt;
            &lt;tr&gt;{columns.map(col =&gt; &lt;th key={`header-${col.heading}`}&gt;{col.heading}&lt;/th&gt;)}&lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
            {data.map(item =&gt;
                &lt;tr key={`${item[propertyAsKey]}-row`}&gt;
                    {columns.map(col =&gt; &lt;td key={`${item[propertyAsKey]}-${col.property}`}&gt;{item[col.property]}&lt;/td&gt;)}
                &lt;/tr&gt;
            )}
        &lt;/tbody&gt;
    &lt;/table&gt;


(You may add brackets and a return statement after the arrow sign, its up to you)

It is also not required to have a fragment as a parent element in your render, as long as there is a single element, it is fine :

class App extends React.Component {
  render() {
    return (
        &lt;Table
          columns={columns}
          data={data}
          propertyAsKey='name' //The data property to be used as a key
        /&gt;
    );
  }
}


Your App component could also be converted to a stateless component:

const App = props =&gt; &lt;Table columns={columns} data={data} propertyAsKey='name' /&gt;