ReactJS中如何从嵌套对象提取键值对并生成动态树形表格?
React 树形层级动态表格实现方案
核心思路
- 用数组
expandedNodes记录所有展开的节点路径,控制层级展开/收起 - 通过节点路径解析API嵌套数据,实现分层渲染
- 根据选中的节点类型(Apps/Hardware),动态生成对应结构的表格
完整代码实现
import { useState } from 'react'; // 模拟API返回的嵌套结构数据 const apiData = { HPC: { ADAS: { Apps: [ { id: 1, name: 'ADAS感知', version: 'v2.3', status: '运行中' }, { id: 2, name: '路径规划', version: 'v1.8', status: '待部署' } ], Hardware: [ { id: 1, model: 'NVIDIA Xavier', serial: 'XAV-001', usage: '75%' }, { id: 2, model: 'Intel Xeon', serial: 'XEON-003', usage: '60%' } ] }, CP: { Apps: [ { id: 1, name: '车辆控制', version: 'v3.1', status: '运行中' }, { id: 2, name: '故障诊断', version: 'v2.5', status: '运行中' } ], Hardware: [ { id: 1, model: 'TI TDA4VM', serial: 'TDA-007', usage: '80%' } ] } } }; const HierarchicalTable = () => { // 存储展开的节点路径,格式如 ['HPC', 'HPC.ADAS'] const [expandedNodes, setExpandedNodes] = useState([]); // 存储当前要展示表格的节点路径 const [activeTableNode, setActiveTableNode] = useState(null); // 切换节点展开/收起状态 const toggleNode = (nodePath) => { setExpandedNodes(prev => prev.includes(nodePath) ? prev.filter(path => path !== nodePath) : [...prev, nodePath] ); // 展开新节点时清空当前表格选中状态 if (!expandedNodes.includes(nodePath)) { setActiveTableNode(null); } }; // 触发表格展示/隐藏 const showTable = (nodePath) => { setActiveTableNode(prev => prev === nodePath ? null : nodePath); }; // 根据节点路径获取对应数据 const getDataByPath = (path) => { return path.split('.').reduce((data, key) => data?.[key], apiData); }; // 递归渲染树形按钮层级 const renderTree = (data, parentPath = '') => { return Object.keys(data).map(key => { const currentPath = parentPath ? `${parentPath}.${key}` : key; const isExpanded = expandedNodes.includes(currentPath); const hasChildren = typeof data[key] === 'object' && !Array.isArray(data[key]); return ( <div key={currentPath} style={{ marginLeft: parentPath ? '20px' : 0, marginTop: '10px' }}> <button onClick={() => hasChildren ? toggleNode(currentPath) : showTable(currentPath)} style={{ padding: '8px 16px', cursor: 'pointer', background: '#f0f0f0', border: '1px solid #ddd', borderRadius: '4px' }} > {isExpanded ? '▼' : '▶'} {key} </button> {/* 渲染子节点 */} {isExpanded && hasChildren && renderTree(data[key], currentPath)} {/* 渲染对应表格 */} {activeTableNode === currentPath && renderTable(data[key])} </div> ); }); }; // 渲染Apps/Hardware表格 const renderTable = (tableData) => { if (!tableData?.length) return null; // 根据数据字段判断表格类型,生成对应表头 const isAppsTable = 'name' in tableData[0]; const tableHeaders = isAppsTable ? ['ID', '应用名称', '版本', '状态'] : ['ID', '硬件型号', '序列号', '使用率']; return ( <div style={{ marginLeft: '40px', marginTop: '10px' }}> <table style={{ borderCollapse: 'collapse', width: '80%' }}> <thead> <tr> {tableHeaders.map(header => ( <th key={header} style={{ border: '1px solid #ddd', padding: '8px', background: '#f5f5f5', textAlign: 'left' }}> {header} </th> ))} </tr> </thead> <tbody> {tableData.map(item => ( <tr key={item.id}> <td style={{ border: '1px solid #ddd', padding: '8px' }}>{item.id}</td> {isAppsTable ? ( <> <td style={{ border: '1px solid #ddd', padding: '8px' }}>{item.name}</td> <td style={{ border: '1px solid #ddd', padding: '8px' }}>{item.version}</td> <td style={{ border: '1px solid #ddd', padding: '8px' }}>{item.status}</td> </> ) : ( <> <td style={{ border: '1px solid #ddd', padding: '8px' }}>{item.model}</td> <td style={{ border: '1px solid #ddd', padding: '8px' }}>{item.serial}</td> <td style={{ border: '1px solid #ddd', padding: '8px' }}>{item.usage}</td> </> )} </tr> ))} </tbody> </table> </div> ); }; return ( <div style={{ padding: '20px' }}> {renderTree(apiData)} </div> ); }; export default HierarchicalTable;
关键细节说明
- 层级状态控制:
expandedNodes数组精准记录每个展开的节点路径,避免状态混乱 - 路径解析逻辑:通过
split('.')和reduce方法,轻松从嵌套API数据中定位目标内容 - 表格自适应:根据数据字段自动匹配表头和列内容,无需硬编码多套表格组件
- 样式优化:通过内联样式实现层级缩进和标准表格边框,确保展示清晰整洁
内容的提问来源于stack exchange,提问作者Abhishek Ambure
相关产品推荐
相关产品推荐

