本文介绍如何将具有参与者、分类、卡片和备注四层语义的嵌套数组数据,重构为扁平化对象数组,并使用 rowSpan 实现跨行合并的响应式表格,确保每个卡片归属关系一目了然。
本文介绍如何将具有参与者、分类、卡片和备注四层语义的嵌套数组数据,重构为扁平化对象数组,并使用 `rowspan` 实现跨行合并的响应式表格,确保每个卡片归属关系一目了然。
在 React 中渲染多层级嵌套数据(如“参与者 → 分类 → 卡片”)为表格时,直接遍历原始嵌套数组易导致结构混乱、语义丢失。最佳实践是先规范化数据结构,再按语义驱动渲染。
原始 participant_data 是混合类型嵌套数组(含字符串、二维数组、三层数组),不利于维护与扩展。应转换为统一的对象数组,例如:
const normalizedData = [ { no: '#1', category: 'not set #0', cards: ['card1', 'A very large title title tile1', /* ... */], comment: 'comment ' }, { no: '#1', category: 'not set #1', cards: ['A very large title title tile2', 'A very large title title tile9'], comment: 'comment ' }, { no: '#1', category: 'name1', cards: ['card1', 'A very large title title tile1', /* ... */], comment: 'comment ' }, // ... 其他条目];
可通过递归或 flatMap 轻松完成转换:
const normalizeParticipantData = (data) => { const [participantId, categories, comment] = data; return categories.flatMap(([categoryName, cards]) => ({ no: participantId, category: categoryName, cards, comment: comment.trim() }));};const sortedData = normalizeParticipantData(participant_data).sort((a, b) => a.no.localeCompare(b.no));
关键在于:同一参与者的多行共享 participant 和 comment 列。React 表格需动态计算 rowSpan 值:
<table className="min-w-full border-collapse"> <thead> <tr> <th className="border px-3 py-2">Participant</th> <th className="border px-3 py-2">Category</th> <th className="border px-3 py-2">Cards</th> <th className="border px-3 py-2">Comment</th> </tr> </thead> <tbody> {sortedData.map((item, index, array) => ( <tr key={`${item.no}-${item.category}-${index}`}> {/* Participant column: only render once per group */} {index === 0 || item.no !== array[index - 1].no ? ( <td rowSpan={array.filter(el => el.no === item.no).length} className="border px-3 py-2 font-medium"> {item.no} </td> ) : null} {/* Category column: always render */} <td className="border px-3 py-2">{item.category}</td> {/* Cards column: render as bullet list for readability */} <td className="border px-3 py-2"> {Array.isArray(item.cards) ? ( <ul className="list-disc pl-5 m-0 space-y-0.5 text-sm"> {item.cards.map((card, i) => ( <li key={i} className="whitespace-normal break-words">{card}</li> ))} </ul> ) : ( String(item.cards) )} </td> {/* Comment column: same logic as Participant */} {index === 0 || item.no !== array[index - 1].no ? ( <td rowSpan={array.filter(el => el.no === item.no).length} className="border px-3 py-2 text-gray-600"> {item.comment} </td> ) : null} </tr> ))} </tbody></table>
通过结构规范化 + 语义化渲染,你不仅能清晰表达“谁(Participant)在哪个分类(Category)下拥有哪些卡片(Cards)”,还能灵活支持排序、搜索、导出等后续功能,真正让复杂嵌套数据“一表读懂”。