I have the following data that I want to iterate over:
const publications = [
{
id: '1',
year: '2022-2023',
period: 'First half',
title: 'My title',
authors: 'John Doe',
type: 'Journal Article',
},
{
id: '2',
year: '2021-2022',
period: 'Second half',
title: 'My second title',
authors: 'Jane Doe',
type: 'Book',
},
{
id: '3',
year: '2022-2023',
period: 'First half',
title: 'My third title',
authors: 'John Smith',
type: 'Review',
},
];
I'm trying to produce an MUI table like this:
<TableContainer component={Paper}>
<TableHead>
<TableRow>
{publicationsData.map((item) => (
<TableCell key={item.id}>{item.name}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{publications.map((publication) => (
<TableRow key={publication.id}>
<TableCell>{publication.year}</TableCell>
<TableCell>{publication.period}</TableCell>
<TableCell>{publication.title}</TableCell>
<TableCell>{publication.authors}</TableCell>
<TableCell>{publication.type}</TableCell>
</TableRow>
))}
</TableBody>
</TableContainer>
I put the name of the columns in a separate json file (publicationsData.json)–I want to eliminate this step if possible.
[
{
"id": "1",
"name": "Grant Year"
},
{
"id": "2",
"name": "Grant Period"
},
{
"id": "3",
"name": "Title"
},
{
"id": "4",
"name": "Author(s)"
},
{
"id": "5",
"name": "Type"
}
]
I was able to eliminate code duplication in the header with the .map
.
How do I do the same with the table body? I don't want to manually copy each <TableCell>
component.
What's the best way to go about this?
CodePudding user response:
You can do this using Object.keys
.
Try replacing this:
{publications.map((publication) => (
<TableRow key={publication.id}>
<TableCell>{publication.year}</TableCell>
<TableCell>{publication.period}</TableCell>
<TableCell>{publication.title}</TableCell>
<TableCell>{publication.authors}</TableCell>
<TableCell>{publication.type}</TableCell>
</TableRow>
))}
with this:
{publications.map((publication) => (
<TableRow key={publication.id}>
{Object.keys(publication).map((key, index) => (
<TableCell>{publication[key]}</TableCell>
))}
</TableRow>
))}
This may not be desirable though, since you'll end up with a TableCell
for id
.
You could prevent this by first filtering the attributes to not include id
like this:
{
publications.map((publication) => (
<TableRow key={publication.id}>
{Object.keys(publication).filter(key => key !== 'id')
.map((key) => (
<TableCell>{publication[key]}</TableCell>
))}
</TableRow>
));
}
At this point though, the effort to write cleaner code may be adding more complexity than is worthwhile. There are tradeoffs to every decision, and in this case, writing <TableCell>{...}</TableCell>
for several lines in a row (even 20 lines in a row) might not be the worst thing ever.
It all depends on your exact use-case of course.
Ordering Problem:
As Yone's comment pointed out, this leaves you without a guarantee of the order the keys will be iterated over.
As an alternative, you can create an array of the columns you want in order, like this:
const columns = ["year", "period", "title", "authors", "type"]
And then you can iterate to create your table cells based on that array:
{
publications.map((publication) => (
<TableRow key={publication.id}>
{columns.map((key) => (
<TableCell>{publication[key]}</TableCell>
))}
</TableRow>
));
}
This reduces complexity and gives you better control over the columns.