Alright alright, i took me around two hours to do it and to get back into react, it's been a while i haven't touched it. So if y'all see something to refactor, with pleasure you can edit !
The code first :
import React from "react";
import "./styles.css";
export default class App extends React.Component {
state = {
columns: [],
columnsToHide: ["_id"],
results: [
{
_id: 1,
firstname: "Robert",
lastname: "Redfort",
city: "New York",
zip: 1233,
street: "Mahn Street",
street_number: "24A",
favoriteKebab: "cow"
},
{
_id: 2,
firstname: "Patty",
lastname: "Koulou",
city: "Los Angeles",
zip: 5654,
street: "Av 5th Central",
street_number: 12
},
{
_id: 3,
firstname: "Matt",
lastname: "Michiolo",
city: "Chicago",
zip: 43452,
street: "Saint Usk St",
street_number: 65,
phoneNumber: "0321454545"
},
{
_id: 4,
firstname: "Sonia",
lastname: "Remontada",
city: "Buenos Aires",
zip: "43N95D",
street: "Viva la Revolution Paso",
street_number: 5446,
country: "Argentina"
}
]
};
componentDidMount() {
this.mappDynamicColumns();
}
mappDynamicColumns = () => {
let columns = [];
this.state.results.forEach((result) => {
Object.keys(result).forEach((col) => {
if (!columns.includes(col)) {
columns.push(col);
}
});
this.setState({ columns });
});
};
addTableRow = (result) => {
let row = [];
this.state.columns.forEach((col) => {
if (!this.state.columnsToHide.includes(col)) {
row.push(
Object.keys(result).map((item) => {
if (result[item] && item === col) {
return result[item];
} else if (item === col) {
return "No Value";
}
})
);
row = this.filterDeepUndefinedValues(row);
}
});
return row.map((item, index) => {
// console.log(item, "item ?");
return (
<td
key={`${item}--${index}`}
className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"
>
{item}
</td>
);
});
};
mapTableColumns = () => {
return this.state.columns.map((col) => {
if (!this.state.columnsToHide.includes(col)) {
const overridedColumnName = this.overrideColumnName(col);
return (
<th
key={col}
scope="col"
className="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{overridedColumnName}
</th>
);
}
});
};
filterDeepUndefinedValues = (arr) => {
return arr
.map((val) =>
val.map((deepVal) => deepVal).filter((deeperVal) => deeperVal)
)
.map((val) => {
if (val.length < 1) {
val = ["-"];
return val;
}
return val;
});
};
// if you want to change the text of the col you could do here in the .map() with another function that handle the display text
overrideColumnName = (colName) => {
switch (colName) {
case "phoneNumber":
return "Phone number";
case "lastname":
return "Custom Last Name";
default:
return colName;
}
};
createTable = (results) => {
return (
<table class="min-w-full divide-y divide-gray-200">
<thead>
<tr>{this.mapTableColumns()}</tr>
</thead>
<tbody>
{results.map((result, index) => {
return <tr key={result._id}>{this.addTableRow(result)}</tr>;
})}
</tbody>
</table>
);
};
render() {
return (
<div class="flex flex-col">
<div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
<div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
{this.state.results.length ? (
<div className="card">
{this.createTable(this.state.results)}
</div>
) : null}
</div>
</div>
</div>
</div>
);
}
}
Why undefined ? Well, like i said, im kind of rotten on react (and maybe on codding ;) ) But i didn't find any other way to keep the orders of each value under her belonging column without it being at her current index in the array.
To come back to our code, we end up with a variable row populated with each values belonging to her current column name, so at the right index.
the payload of row before being sending back on the html is so :
As you see, it isn't clean. But i did not find any solution for that, i believe it's possible but it requires more time to think.
So since it's really deeply nested, i had to come up with some filters, to allow the user to see when the property doens't exist, or we could just left it blank.
After each push, i need to clean my deep nested values in arrays, so i've come up with a function filterDeepUndefinedValues
row = this.filterDeepUndefinedValues(row)
And the function itself
filterDeepUndefinedValues = (arr) => {
return arr
.map((val) => val.map((deepVal) => deepVal).filter((deeperVal) => deeperVal))
.map((val) => {
if (val.length < 1) {
val = ["-"];
return val;
}
return val;
});
};
To make short, i have arrays nested in array which contain undefined values, so i'll return a new array filtered out without undefined value, so it return an empty array.
How the array is received in the function initially

In the second part, i just replace empty array content by a hyphen in array
First treatment with .map and filter()

Replace empty array with a hyphen

The return of addTableRow
return row.map((item, index) => {
return (
<td
key={`${item}--${index}`}
className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"
>
{item}
</td>
);
});
Here we simply map all the values from rows and render a td for each array of row
Im running out of time to end the details, but all it's here and i will try to come back later to clean a bit this answer and it's grammar.
Codesandbox link: https://codesandbox.io/s/bold-mendel-vmfsk?file=/src/App.js