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

使用React-Window搭配MUI可折叠表格行出现隐藏内容错位问题求助

React-Window搭配MUI可折叠表格行错位解决方案

根本原因

  • react-window 基于预设行高计算每个虚拟行的偏移位置,默认不会主动监听行高动态变化
  • 原有实现中单个虚拟行索引对应两个<TableRow>元素,且两个元素均应用了react-window传入的定位样式,导致定位逻辑冲突
  • 展开状态变更时,未通知react-window重新计算对应索引后所有行的偏移量,最终出现位置错位

修复方案

1. 替换虚拟滚动组件

使用VariableSizeList替代FixedSizeList,支持动态行高配置。

2. 统一维护展开状态

将行展开状态从Row组件内部提到父组件维护,方便全局计算每行高度,同时在状态变更时触发react-window的位置重计算。

3. 调整Row组件结构

将同索引下的两个<TableRow>统一包裹在同一个容器元素中,仅给外层容器应用react-window传入的定位样式。

代码示例

父组件实现

import { useState, useRef } from 'react';
import { VariableSizeList } from 'react-window';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableRow from '@mui/material/TableRow';
import TableCell from '@mui/material/TableCell';
import IconButton from '@mui/material/IconButton';
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';

const VirtualTable = () => {
  // 维护所有行的展开状态
  const [expandedRows, setExpandedRows] = useState({});
  // 存储VariableSizeList实例,用于触发重计算
  const listRef = useRef(null);
  // 示例数据,可替换为实际业务数据
  const tableData = new Array(100).fill(null);

  // 动态计算每行高度:普通行高52px(MUI TableRow默认高度),展开状态额外加52px
  const getRowHeight = (index) => {
    return expandedRows[index] ? 104 : 52;
  };

  // 切换行展开状态
  const toggleRowExpand = (index) => {
    setExpandedRows(prev => {
      const newState = {...prev, [index]: !prev[index]};
      // 通知react-window重计算当前索引后所有行的位置
      listRef.current?.resetAfterIndex(index);
      return newState;
    });
  };

  const Row = ({ index, style }) => (
    // 仅外层容器应用react-window的定位样式
    <div style={style}>
      <TableRow>
        <TableCell>可见行:{index}</TableCell>
        <TableCell>
          <IconButton
            aria-label="expand row"
            size="small"
            onClick={() => toggleRowExpand(index)}
          >
            {expandedRows[index] ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
          </IconButton>
        </TableCell>
      </TableRow>
      <TableRow sx={{ display: expandedRows[index] ? "table-row" : "none" }}>
        <TableCell>隐藏行:{index}</TableCell>
      </TableRow>
    </div>
  );

  return (
    <Table>
      <TableBody component={div}>
        <VariableSizeList
          ref={listRef}
          height={600}
          itemCount={tableData.length}
          itemSize={getRowHeight}
          width="100%"
        >
          {Row}
        </VariableSizeList>
      </TableBody>
    </Table>
  );
};

补充说明

如果行高不是固定值,可以通过useRef获取实际DOM渲染后的高度,更新到getRowHeight的计算逻辑中,注意需要在展开状态变更、DOM渲染完成后再读取高度,之后调用resetAfterIndex触发重计算即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 16:45:07