You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React表格行同步折叠问题:如何实现点击行单独折叠展开?

解决方案

问题根源在于你用了一个全局布尔值open控制所有行的折叠状态,所有行共享同一状态,所以点击任意一行都会同步切换所有行的展开/折叠。要实现单独控制每一行,需要为每个行维护独立的展开状态,具体修改步骤如下:


1. 修改状态定义

把单个布尔值状态改成数组,用来存储所有处于展开状态的行的唯一标识(优先用行的id,如果没有则用city,确保标识唯一):

// 用数组存储展开行的唯一标识,初始为空数组
const [expandedRows, setExpandedRows] = useState([]);

2. 修改点击事件逻辑

点击图标时,切换当前行的展开状态(已展开就从数组移除,未展开则添加到数组):

<IconButton
  aria-label="expand row"
  size="small"
  onClick={() => {
    setExpandedRows(prevState => 
      prevState.includes(row.city) 
        ? prevState.filter(item => item !== row.city) // 已展开则移除
        : [...prevState, row.city] // 未展开则添加
    );
  }}
>

3. 更新折叠组件和图标判断

将Collapse的in属性和图标显示逻辑,从全局open改成判断当前行的标识是否在expandedRows数组中:

// 图标切换逻辑
{expandedRows.includes(row.city) ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}

// Collapse组件的展开条件
<Collapse in={expandedRows.includes(row.city)} timeout="auto" unmountOnExit>

修改后的完整代码片段

const [expandedRows, setExpandedRows] = useState([]);

// ...其他代码

<TableBody> {isCityReady && cities ? (cities.map((row, index) => (
<TableRow key={index} sx={{ borderBottom: "2px solid black", "&:last-child td, &:last-child th": { border: 0 } }}>
    <TableCell>
        <IconButton
        aria-label="expand row"
        size="small"
        onClick={() => {
          setExpandedRows(prevState => 
            prevState.includes(row.city) 
              ? prevState.filter(item => item !== row.city)
              : [...prevState, row.city]
          );
        }}
        >
            {expandedRows.includes(row.city) ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
        </IconButton>
    </TableCell>
    <TableCell component="th" scope="row" align="left">
        <Typography variant="body1" fontWeight="bold">
            {row.city}
        </Typography>
    </TableCell>
    <TableCell>
        <Collapse in={expandedRows.includes(row.city)} timeout="auto" unmountOnExit>
            <Typography variant="body1" fontWeight="bold"> 
            {isReady && locations ? (locations.filter(rowLocation => rowLocation.city === row.city).map((rowLocation, indexLocation) => (
                <TableRow key={indexLocation} sx={{ "&:last-child td, &:last-child th": { border: 0 } }}>
                    <TableCell style={{ width: '100%' }}>
                        <Typography variant="body1">
                            {rowLocation.office}
                        </Typography>
                    </TableCell>
                    <TableCell align="right" style={{ width: '100%' }}>
                        <Button variant="contained" color="primary" onClick={() => handleOpenLocationsDialog("Edit", rowLocation.city, rowLocation.office, rowLocation.id)}>
                            Edit
                        </Button>
                    </TableCell>
                </TableRow>
            ))) : (<LocationsTableSkeleton />)}
            </Typography>
        </Collapse>
    </TableCell>
</TableRow>
))
) : (
    <LocationsTableSkeleton />
)}

额外优化建议

如果你的row对象有唯一的id字段,建议用row.id代替row.city作为标识,避免出现多个行城市名称相同导致状态混乱的情况。

内容的提问来源于stack exchange,提问作者PaulPolon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 03:15:38