ReactJS与Material UI TreeView如何渲染不同字段名多层JSON数据
问题场景
你需要将多层嵌套、各层级子节点对应字段名不一致的对象数组传入TreeView组件实现层级展示,原始数据结构如下:
export const application_group_one = [ { id: uniqueId(), name: "Application 1", organizations: [ { id: uniqueId(), name: "Organization 1", artifacts: [ { id: uniqueId(), name: "Artifact 1", }, ], }, ], }, { id: uniqueId(), name: "Application 2", organizations: [ { id: uniqueId(), name: "Organization 1", artifacts: [], }, ], }, ]; export const application_group_two = [ { id: uniqueId(), name: "Application 3", organizations: [ { id: uniqueId(), name: "Organization 1", artifacts: [ { id: uniqueId(), name: "Artifact 1", }, { id: uniqueId(), name: "Artifact 2", }, { id: uniqueId(), name: "Artifact 3", }, ], }, { id: uniqueId(), name: "Organization 2", artifacts: [ { id: uniqueId(), name: "Artifact 1", }, { id: uniqueId(), name: "Artifact 2", }, { id: uniqueId(), name: "Artifact 3", }, ], }, { id: uniqueId(), name: "Organization 3", artifacts: [ { id: uniqueId(), name: "Artifact 1", }, { id: uniqueId(), name: "Artifacy 2", }, { id: uniqueId(), name: "Artifact 3", }, ], }, { id: uniqueId(), name: "Organization 4", artifacts: [ { id: uniqueId(), name: "Artifact 1", }, { id: uniqueId(), name: "Artifact 2", }, { id: uniqueId(), name: "Artifact 3", }, ], }, ], }, ];
现有实现仅能正常渲染Organizations层级,无法展示artifacts层级,原有代码如下:
const getTreeItemsFromData = (treeItems) => { return treeItems.map((treeItemData) => { let organizations = undefined; if (treeItemData.organizations && treeItemData.organizations.length > 0) { organizations = getTreeItemsFromData(treeItemData.organizations); } return <TreeItem key={treeItemData.id} nodeId={treeItemData.id} label={treeItemData.name} children={organizations} />; }); }; const DataTreeView = ({ treeItems }) => { return ( <TreeView defaultCollapseIcon={<ExpandMoreIcon />} defaultExpandIcon={<ChevronRightIcon />}> {getTreeItemsFromData(treeItems)} </TreeView> ); }; export const Tree = () => { return ( <div className="App"> <DataTreeView treeItems={application_group_one} /> <br /> <DataTreeView treeItems={application_group_two} /> </div> ); };
问题根因
原有递归逻辑硬编码了子节点字段名为organizations,没有对artifacts字段做处理,这种硬编码字段名的写法既无法覆盖现有三层结构的渲染需求,也不能兼容后续新增层级的场景。
修复方案
放弃硬编码子节点字段名的写法,通过通用特征自动识别节点下的子节点集合:只要是当前节点属性中值为非空数组、且数组元素包含id和name字段,就判定为子节点列表,传入递归逻辑渲染即可。
修复后的完整代码如下:
const getTreeItemsFromData = (treeItems) => { return treeItems.map((treeItemData) => { let children = undefined; // 遍历当前节点所有属性,自动识别子节点数组 Object.values(treeItemData).forEach(val => { if ( Array.isArray(val) && val.length > 0 && typeof val[0] === 'object' && 'id' in val[0] && 'name' in val[0] ) { children = getTreeItemsFromData(val); } }); return <TreeItem key={treeItemData.id} nodeId={treeItemData.id} label={treeItemData.name} children={children} />; }); }; const DataTreeView = ({ treeItems }) => { return ( <TreeView defaultCollapseIcon={<ExpandMoreIcon />} defaultExpandIcon={<ChevronRightIcon />}> {getTreeItemsFromData(treeItems)} </TreeView> ); }; export const Tree = () => { return ( <div className="App"> <DataTreeView treeItems={application_group_one} /> <br /> <DataTreeView treeItems={application_group_two} /> </div> ); };
方案特性
- 无需提前声明各层级子节点的字段名,自动适配
organizations、artifacts等任意命名的子节点字段 - 支持无限层级嵌套,后续如果在artifacts下新增versions、builds等层级,不需要修改渲染逻辑即可直接展示
- 兼容原有代码的使用方式,不需要修改传入的数据源结构
- 如果业务场景中存在单个节点下挂载多个子节点数组的情况,可以调整子节点识别逻辑,将多个符合条件的数组合并后再传入递归即可。
内容的提问来源于stack exchange,提问作者Drew Pepin
相关产品推荐
相关产品推荐

