TypeScript实现点击箭头展开折叠嵌套表格问题求解
问题修复方案
你的代码存在4个核心问题导致嵌套表格无法正常渲染、交互异常:
- 行切换逻辑错误:
toggleRow固定将行状态设为true,无法实现点击折叠 - 箭头图标判断逻辑错误:直接判断
openRows对象(永远为真),没有读取对应行的展开状态 - 表格DOM结构违规:将折叠内容、明细单元格直接塞在箭头列的
TableCell内部,不符合HTML表格嵌套规范,也没有渲染主行的姓名基础字段 - 嵌套表格未做列跨域处理:展开的明细行需要占满整行宽度,必须设置
colSpan匹配总列数
修复后完整代码
import { useState } from 'react'; import { Paper, TableContainer, Table, TableHead, TableRow, TableCell, Typography, TableBody, IconButton, Collapse, Box, } from '@mui/material'; import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp'; // 原有类型定义保留 export interface UserData { userId: string; firstName: string; lastName: string; jobDetails: { company: string; title: string; }; } interface UserDataProps { data: UserData[]; classes: { container: string; tableRoot: string; cell: string; tableRow: string; hover: string; arrowCell: string; nestedTable: string; }; } const UserTable = ({ data, classes }: UserDataProps) => { const columns = ["First Name", "Last Name"]; const [openRows, setOpenRows] = useState<Record<string, boolean>>({}); // 修复切换逻辑:点击时取反当前行状态 const toggleRow = (userId: string) => setOpenRows((prevState) => ({ ...prevState, [userId]: !prevState[userId], })); return ( <Paper elevation={3} variant="outlined"> <TableContainer className={classes.container}> <Table stickyHeader classes={{ root: classes.tableRoot }}> <colgroup> <col width="5%" /> <col width="10%" /> <col width="15%" /> </colgroup> <TableHead> <TableRow> {/* 单独给箭头控制列预留表头位置,避免列数错位 */} <TableCell className={classes.cell} role="columnheader" /> {columns.map((column) => ( <TableCell key={column} className={classes.cell} role="columnheader" > <Typography variant="h6">{column}</Typography> </TableCell> ))} </TableRow> </TableHead> <TableBody> {data.map((dataRow) => { const isRowOpen = !!openRows[dataRow.userId]; return ( <> {/* 主数据行 */} <TableRow key={dataRow.userId} title="tableRow" className={classes.tableRow} classes={{ hover: classes.hover }} hover > <TableCell classes={{ root: classes.arrowCell }}> <IconButton size="small" onClick={() => toggleRow(dataRow.userId)} > {/* 修复箭头判断:展开显示向下箭头,折叠显示向上箭头 */} {isRowOpen ? <ArrowDropDownIcon /> : <ArrowDropUpIcon />} </IconButton> </TableCell> {/* 补全主行缺失的基础数据单元格 */} <TableCell>{dataRow.firstName}</TableCell> <TableCell>{dataRow.lastName}</TableCell> </TableRow> {/* 展开明细行:独立TableRow,colSpan=3占满整行宽度 */} <TableRow> <TableCell colSpan={3} style={{ paddingBottom: 0, paddingTop: 0 }}> <Collapse in={isRowOpen} timeout="auto" unmountOnExit > <Box margin={1}> {/* 嵌套独立表格渲染明细,符合表格结构规范 */} <Table size="small" className={classes.nestedTable}> <TableHead> <TableRow> <TableCell>公司</TableCell> <TableCell>职位</TableCell> </TableRow> </TableHead> <TableBody> <TableRow> <TableCell>{dataRow.jobDetails.company}</TableCell> <TableCell>{dataRow.jobDetails.title}</TableCell> </TableRow> </TableBody> </Table> </Box> </Collapse> </TableCell> </TableRow> </> ); })} </TableBody> </Table> </TableContainer> </Paper> ); }; export default UserTable;
关键改动说明
- 调整表头结构,单独为箭头控制列预留位置,避免列数不对齐
- 每行拆分为主数据行、明细行两个独立
TableRow,明细行通过colSpan占满所有列宽度,不破坏表格布局 - 嵌套明细使用独立
Table组件渲染,符合HTML表格嵌套规则,不会出现单元格错位 - 修复状态切换、箭头判断的逻辑错误,点击可正常展开/折叠
- 补全主行原本缺失的姓名字段渲染
如果明细是多行列表,直接在嵌套表格的
TableBody中循环渲染对应TableRow即可,逻辑和主表一致。
内容的提问来源于stack exchange,提问作者Tanu
相关产品推荐
相关产品推荐

