React-virtualized:calcRowHeight返回0时忽略行渲染的问题求助
Problem Description
I'm using react-virtualized's MultiGrid component to render a large dataset. My current approach uses a toggle state to show/hide rows by returning 0 or 25 in calcRowHeight, and triggers recomputeGridSize when the toggle changes. This works fine for small datasets, but with large ones (e.g. hiding the first 2000 rows), react-virtualized still renders those 0-height rows, causing browser memory overload. I can't modify the original dataset, so how do I make react-virtualized skip rendering rows when calcRowHeight would return 0?
Here's my current code:
class ImportContainer extends React.Component<IImportContainerProps, IImportContainerState> { grid; componentDidUpdate(prevProps) { if (prevProps.toggleState !== this.props.toggleState) { this.refreshGridRows(0, 0); } } componentWillUnmount() {} refreshGridRows = (start: number = 0, end: number = (this.state.gridRowCount - this.state.fixedRowCount)) => { this.grid.recomputeGridSize({columnIndex: this.state.fixedColumnCount, rowIndex: 0}); } calcRowHeight = (I) => { if (this.state.myData[I.index].randomBool === this.props.toggleState) { return 0; } else { return 25; } } render() { return <> <AutoSizer> {({ height, width }) => <MultiGrid ref={this._setRef} rowHeight={this.calcRowHeight} [...] />} </AutoSizer> </>; } } export default ImportContainer;
Solution: Map Visible Rows Instead of Using 0 Height
I've run into this exact issue before—returning 0 from calcRowHeight doesn't prevent react-virtualized from creating DOM elements for those rows; it just makes them invisible. For large datasets, this adds up to massive memory bloat. Instead, we need to only pass the rows that should be rendered to MultiGrid, while maintaining a mapping to the original dataset indices.
Here's how to implement this:
1. Track Visible Row Indices
Add a state variable to store the indices of rows that should be visible in the original dataset:
state = { // ... your existing state visibleRowIndices: [] };
2. Compute Visible Rows on Toggle Change
Update your logic to recalculate visibleRowIndices whenever toggleState changes, instead of just calling recomputeGridSize:
componentDidMount() { // Initialize visible rows when the component mounts this.updateVisibleRows(); } componentDidUpdate(prevProps) { if (prevProps.toggleState !== this.props.toggleState) { this.updateVisibleRows(); } } updateVisibleRows = () => { const { myData } = this.state; const { toggleState } = this.props; // Filter original indices to only include rows that should be shown const visibleIndices = myData .map((_, index) => index) .filter(index => myData[index].randomBool !== toggleState); this.setState({ visibleRowIndices: visibleIndices }, () => { // After state updates, tell MultiGrid to recompute layout sizes if (this.grid) { this.grid.recomputeGridSize({ columnIndex: this.state.fixedColumnCount, rowIndex: 0 }); } }); };
3. Adjust MultiGrid Props
Modify the MultiGrid props to use the visible row count, and map the rendered row index back to the original dataset index:
- Set
rowCounttovisibleRowIndices.length(so MultiGrid only renders actual visible rows) - Update cell renderers to use
visibleRowIndices[rowIndex]to fetch the original data - Replace
calcRowHeightwith a fixed value (since we're no longer hiding rows via height)
4. Updated Full Code
class ImportContainer extends React.Component<IImportContainerProps, IImportContainerState> { grid; state = { // ... your existing state (myData, fixedRowCount, fixedColumnCount, gridRowCount) visibleRowIndices: [] }; componentDidMount() { this.updateVisibleRows(); } componentDidUpdate(prevProps) { if (prevProps.toggleState !== this.props.toggleState) { this.updateVisibleRows(); } } componentWillUnmount() {} updateVisibleRows = () => { const { myData } = this.state; const { toggleState } = this.props; const visibleIndices = myData .map((_, index) => index) .filter(index => myData[index].randomBool !== toggleState); this.setState({ visibleRowIndices: visibleIndices }, () => { if (this.grid) { this.grid.recomputeGridSize({ columnIndex: this.state.fixedColumnCount, rowIndex: 0 }); } }); }; // Fixed row height since we only render visible rows now calcRowHeight = () => 25; _setRef = (ref) => { this.grid = ref; }; render() { const { visibleRowIndices, fixedRowCount, fixedColumnCount } = this.state; return ( <AutoSizer> {({ height, width }) => ( <MultiGrid ref={this._setRef} rowHeight={this.calcRowHeight} rowCount={visibleRowIndices.length} fixedRowCount={fixedRowCount} fixedColumnCount={fixedColumnCount} // Map rendered row index back to original dataset index cellRenderer={({ columnIndex, rowIndex, key, style }) => { const originalRowIndex = visibleRowIndices[rowIndex]; const rowData = this.state.myData[originalRowIndex]; return ( <div key={key} style={style}> {/* Render your cell content using rowData */} </div> ); }} {.../* Your other MultiGrid props */} /> )} </AutoSizer> ); } } export default ImportContainer;
Key Notes
- Fixed Rows Handling: If you have fixed rows that shouldn't be filtered out, adjust the
visibleRowIndiceslogic to prepend those fixed indices to the filtered array. - Performance Optimization: For extremely large datasets, consider memoizing the
visibleRowIndicescalculation (usinguseMemoif converting to functional components, orshouldComponentUpdatefor class components) to avoid unnecessary re-computations. - Memory Relief: By only passing rows that need to be rendered, react-virtualized won't create DOM elements for hidden rows, completely eliminating the memory overhead you were seeing.
内容的提问来源于stack exchange,提问作者pierreaurelemartin

