React递归渲染任意层级嵌套数据:手风琴UI与分页实现
处理嵌套数据的React通用递归手风琴组件与分页实现
技术需求与问题描述
需要在React中实现通用递归函数,将任意层级的嵌套数据集渲染为可展开/收起的树形手风琴UI。此前使用map的实现不具备通用性,无法适配动态嵌套结构。同时需为laboratory、medications这类包含多对象的节点添加分页功能。
数据集示例
const data = [ { "laboratory": [ { "status": { display: "Code", value: "23123" }, "observation": [ { display: "Code", value: "23123" } ], "resultValue": { "quantity": [ { display: "Code", value: "23123" } ], "codeableConcept": [ { display: "Code", value: "23123" }, { display: "Code", value: "23123" } ] } }, { "status": { display: "Code", value: "23123" }, "observation": [ { display: "Code", value: "23123" } ], "resultValue": { "quantity": [ { display: "Code", value: "23123" } ], "codeableConcept": [ { display: "Code", value: "23123" }, { display: "Code", value: "23123" } ] } } ] }, { "medications": [ { "status": { display: "medications - Code", value: "23123" }, "resultValue": { "quantity": [ { display: "medications- Code", value: "23123" }, { display: "medications-Code", value: "23123" } ], "codeableConcept": [ { display: "medications-Code", value: "23123" }, { display: "medications- Code", value: "23123" } ] } }, { "status": { display: "medications - Code", value: "23123" }, "resultValue": { "quantity": [ { display: "medications- Code", value: "23123" } ], "codeableConcept": [ { display: "medications-Code", value: "23123" } ] } }, { "status": { display: "medications - Code", value: "23123" }, "resultValue": { "quantity": [ { display: "medications- Code", value: "23123" } ], "codeableConcept": [ { display: "medications-Code", value: "23123" } ] } } ] } ]
期望UI展示(手风琴式树形结构)
-> Laboratory(Main Heading) <h1> Laboratory</h1> -> status (Sub-heading) <h3>status</h3> Code - 23123 (table data) <table><th>Code</th><tr></tr>23123</table> -> observation (Sub-heading) Code - 23123 (table data) -> ResultValue (Sub-heading) -> quantity (Sub-sub heading) Code - 23123 (table data) -> codeableConcept (Sub-sub heading) Code - 23123 (table data) Code - 23123 (table data) -> medications(Main Heading) -> status (Sub-heading) medications - Code - 23123 (table data) -> ResultValue (Sub-heading) -> quantity (Sub-sub heading) medications- Code - 23123 (table data) medications-Code - 23123 (table data) -> codeableConcept (Sub-sub heading) medications-Code - 23123 (table data) medications- Code - 23123 (table data)
现有尝试代码(非通用)
data.map((data) => { Object.keys(data).map((header) => { //console.log(header) data[header].map((headerData) => { if(Array.isArray(data[header])) { //console.log(headerData) } else { console.log(headerData) } }) }) })
核心问题
- 如何编写通用递归代码,处理任意n层级的嵌套数据渲染,避免代码僵化?
- 如何为
laboratory、medications这类包含多个对象的节点实现分页功能?
解决方案
1. 通用递归手风琴组件实现
编写可复用的递归组件,通过判断数据类型(对象、数组、叶子节点)动态渲染结构,用状态管理展开/收起:
import { useState } from 'react'; // 判断是否为叶子节点(包含display和value属性) const isLeafNode = (item) => { return typeof item === 'object' && item !== null && 'display' in item && 'value' in item; }; const RecursiveAccordion = ({ data, level = 0 }) => { const [isExpanded, setIsExpanded] = useState(true); // 根据层级匹配标题标签 const getHeadingTag = (level) => { switch(level) { case 0: return 'h1'; case 1: return 'h3'; case 2: return 'h4'; default: return 'h5'; } }; const renderContent = () => { if (Array.isArray(data)) { return data.map((item, index) => ( <RecursiveAccordion key={index} data={item} level={level + 1} /> )); } else if (typeof data === 'object' && data !== null) { return Object.entries(data).map(([key, value]) => ( <div key={key} style={{ marginLeft: `${level * 20}px` }}> <RecursiveAccordion data={{ [key]: value }} level={level + 1} /> </div> )); } else if (isLeafNode(data)) { // 渲染叶子节点表格 return ( <table border="1" style={{ marginLeft: `${level * 20}px`, marginTop: '8px' }}> <thead> <tr> <th>{data.display}</th> </tr> </thead> <tbody> <tr> <td>{data.value}</td> </tr> </tbody> </table> ); } return null; }; // 获取当前节点标题 const getTitle = () => { if (typeof data === 'object' && data !== null) { const keys = Object.keys(data); if (keys.length === 1) { return keys[0].charAt(0).toUpperCase() + keys[0].slice(1); } } return ''; }; const HeadingTag = getHeadingTag(level); return ( <div className="accordion-item"> {getTitle() && ( <HeadingTag onClick={() => setIsExpanded(!isExpanded)} style={{ cursor: 'pointer' }}> {isExpanded ? '↓' : '→'} {getTitle()} </HeadingTag> )} {isExpanded && renderContent()} </div> ); }; // 主组件调用 const App = () => { return ( <div className="app"> {data.map((item, index) => ( <RecursiveAccordion key={index} data={item} /> ))} </div> ); }; export default App;
2. 分页功能实现
针对指定节点封装分页逻辑,在递归组件中判断节点key启用分页:
import { useState } from 'react'; const PagedAccordion = ({ title, items, level }) => { const [isExpanded, setIsExpanded] = useState(true); const [currentPage, setCurrentPage] = useState(1); const itemsPerPage = 2; const totalPages = Math.ceil(items.length / itemsPerPage); // 计算当前页显示条目 const currentItems = items.slice( (currentPage - 1) * itemsPerPage, currentPage * itemsPerPage ); const handlePageChange = (page) => { setCurrentPage(page); }; return ( <div> <h1 onClick={() => setIsExpanded(!isExpanded)} style={{ cursor: 'pointer' }}> {isExpanded ? '↓' : '→'} {title} (Page {currentPage}/{totalPages}) </h1> {isExpanded && ( <> {currentItems.map((item, index) => ( <RecursiveAccordion key={index} data={item} level={level + 1} /> ))} <div style={{ marginLeft: `${level * 20}px`, marginTop: '16px' }}> {Array.from({ length: totalPages }, (_, i) => i + 1).map(page => ( <button key={page} onClick={() => handlePageChange(page)} disabled={page === currentPage} style={{ margin: '0 4px', padding: '4px 8px' }} > {page} </button> ))} </div> </> )} </div> ); }; // 修改递归组件,加入分页判断 const RecursiveAccordion = ({ data, level = 0 }) => { const [isExpanded, setIsExpanded] = useState(true); const paginatedKeys = ['laboratory', 'medications']; // 匹配需要分页的节点 if (typeof data === 'object' && data !== null) { const keys = Object.keys(data); if (keys.length === 1 && paginatedKeys.includes(keys[0])) { const key = keys[0]; return ( <PagedAccordion title={key.charAt(0).toUpperCase() + key.slice(1)} items={data[key]} level={level} /> ); } } // 剩余逻辑与原RecursiveAccordion一致... };
内容的提问来源于stack exchange,提问作者Vivek
相关产品推荐
相关产品推荐

