使用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
相关产品推荐
相关产品推荐

