如何在React中基于嵌套JSON实现无第三方库的树形表格
React 无第三方库实现嵌套树形表格(基于嵌套JSON)
原始嵌套JSON数据
[ { "id": 1, "name": "VehicleData", "owner": "admin", "sub_details": [ { "sub_name": "Lexus", "sub_id": 4 }, { "sub_name": "BMW", "sub_id": 3 } ] }, { "id": 2, "name": "Mobiles", "owner": "admin", "sub_details": [ { "sub_name": "Apple", "sub_id": 2 } ] }, { "id": 3, "name": "Laptop", "owner": "admin", "sub_details": [] } ]
现有基础表格代码
import React, {useState, useEffect} from 'react'; import classes from './TableData.module.css'; const TableData = () => { const [oldData, newData] = useState([]); useEffect(() => { fetchData(); }, []); const Id = (dataId) => { alert(dataId); }; const Name = (dataName) => { alert(dataName); }; const fetchData = async () => { try { const response = await fetch('someapi/json'); const data = await response.json(); newData(data); } catch (e) { console.warn(e) } } const DisplayData = oldData.map((data) => { return ( <tr key={data.id}> <td>{data.id}</td> <td>{data.name}</td> <td>{data.owner}</td> <td> <button onClick={() => Id(data.id)}>getId</button> <button onClick={() => Name(data.name)}>getname</button> </td> </tr> ) }) return ( <div> <div> <table> <thead> <tr> <th>id</th> <th>name</th> <th>owner</th> </tr> </thead> <tbody>{DisplayData}</tbody> </table> </div> </div> ) } export default TableData;
需求解决方案
1. 实现嵌套结构并为每行添加按钮
通过递归渲染逻辑处理父行与子行:编写renderRow函数,先渲染父级数据行,再根据父行的sub_details字段渲染子级行,子行通过样式缩进区分层级;同时为父、子行都添加操作按钮。
2. 点击子行按钮同时获取父子数据
渲染子行时,直接将父级数据作为参数传递给按钮的点击回调函数,在回调中即可同时获取父、子两级数据。
3. 实现展开/收起切换
用expandedIds状态存储当前展开的父项ID集合,点击展开/收起按钮时,切换对应ID的状态,控制子行的显示与隐藏。
完整实现代码
import React, { useState, useEffect } from 'react'; import classes from './TableData.module.css'; const TableData = () => { const [data, setData] = useState([]); // 用Set存储展开的父项ID,方便增删操作 const [expandedIds, setExpandedIds] = useState(new Set()); useEffect(() => { fetchData(); }, []); const fetchData = async () => { try { // 替换为你的真实API地址,这里用模拟数据演示 // const response = await fetch('someapi/json'); // const resData = await response.json(); const resData = [ { "id": 1, "name": "VehicleData", "owner": "admin", "sub_details": [ { "sub_name": "Lexus", "sub_id": 4 }, { "sub_name": "BMW", "sub_id": 3 } ] }, { "id": 2, "name": "Mobiles", "owner": "admin", "sub_details": [ { "sub_name": "Apple", "sub_id": 2 } ] }, { "id": 3, "name": "Laptop", "owner": "admin", "sub_details": [] } ]; setData(resData); } catch (e) { console.warn(e); } }; // 切换父项的展开/收起状态 const toggleExpand = (id) => { const newExpandedIds = new Set(expandedIds); newExpandedIds.has(id) ? newExpandedIds.delete(id) : newExpandedIds.add(id); setExpandedIds(newExpandedIds); }; // 父行按钮点击回调:获取父级数据 const handleParentClick = (parent) => { alert(`父ID: ${parent.id}, 父名称: ${parent.name}`); }; // 子行按钮点击回调:同时获取父级与子级数据 const handleChildClick = (parent, child) => { alert(`父ID: ${parent.id}, 父名称: ${parent.name}\n子ID: ${child.sub_id}, 子名称: ${child.sub_name}`); }; // 递归渲染父行与子行 const renderRow = (parentItem) => { const isExpanded = expandedIds.has(parentItem.id); return ( <> {/* 父级行 */} <tr key={parentItem.id} className={classes.parentRow}> <td>{parentItem.id}</td> <td> {/* 仅当有子项时显示展开/收起按钮 */} {parentItem.sub_details.length > 0 && ( <button onClick={() => toggleExpand(parentItem.id)} className={classes.toggleBtn}> {isExpanded ? '▼' : '▶'} </button> )} {parentItem.name} </td> <td>{parentItem.owner}</td> <td> <button onClick={() => handleParentClick(parentItem)}>获取父数据</button> </td> </tr> {/* 子级行:仅展开状态下渲染 */} {isExpanded && parentItem.sub_details.map(child => ( <tr key={child.sub_id} className={classes.childRow}> <td>{child.sub_id}</td> <td style={{ paddingLeft: '24px' }}>{child.sub_name}</td> <td>{parentItem.owner}</td> <td> <button onClick={() => handleChildClick(parentItem, child)}>获取父子数据</button> </td> </tr> ))} </> ); }; return ( <div className={classes.tableContainer}> <table className={classes.table}> <thead> <tr> <th>ID</th> <th>名称</th> <th>所有者</th> <th>操作</th> </tr> </thead> <tbody> {data.map(item => renderRow(item))} </tbody> </table> </div> ); }; export default TableData;
配套样式参考(TableData.module.css)
.tableContainer { margin: 20px; } .table { border-collapse: collapse; width: 100%; border: 1px solid #ddd; } .table th, .table td { border: 1px solid #ddd; padding: 8px; text-align: left; } .table th { background-color: #f2f2f2; } .parentRow { background-color: #f9f9f9; } .childRow { background-color: #ffffff; } .toggleBtn { margin-right: 8px; border: none; background: transparent; cursor: pointer; }
关键逻辑说明
- 嵌套渲染:通过
renderRow函数递归处理父项与子项,子行通过padding-left实现视觉层级缩进。 - 展开/收起控制:用
Set存储展开的父ID,点击按钮时切换对应ID的状态,动态控制子行的渲染。 - 父子数据传递:渲染子行时直接将父项数据传入点击回调,实现一次获取两级数据。
内容的提问来源于stack exchange,提问作者Madpop
相关产品推荐
相关产品推荐

