Вам поможет rowSpan. В примере я засунул данные в state, но вы продолжайте брать из props:
render() {
return (
<table>
<tbody>
<tr>
{new Array(8).fill(0).map((el, i) => <th key={i}>TEXT</th>)}
</tr>
</tbody>
<tbody>
{this.state['vmBackup']['clouds'].flatMap(cloud => {
return cloud.shards.map(shard => {
const jobCount = shard['jobs'].length;
return shard['jobs'].map((job, i) => {
if (i == 0) {
return (
<tr key={i}>
<td rowSpan={jobCount}>{cloud['cloud']}</td>
<td rowSpan={jobCount}>{shard['repository']}</td>
<td rowSpan={jobCount}>{shard['repository_size_gb']}</td>
<td>{job['job']}</td>
<td>{job['job_description']}</td>
<td>{job['backup_name']}</td>
<td>{job['backup_size_gb']}</td>
<td>5</td>
</tr>
)
} else {
return (
<tr key={i}>
<td>{job['job']}</td>
<td>{job['job_description']}</td>
<td>{job['backup_name']}</td>
<td>{job['backup_size_gb']}</td>
<td>5</td>
</tr>
)
}
})
})
})}
</tbody>
</table>
)
}