react-virtualized: Bug with scrolling with WindowScroller -> AutoSizer -> Table and a big Header

When Table component is used in conjunction with WindowScroller and AutoSizer, there is a bug with empty space on top of the table when the user is scrolling content down. But it works fine when content is being scrolled up.

Gif: http://www.giphy.com/gifs/l4FGCdxL20UsKANgs

Here is a Plunkr to reproduce: https://plnkr.co/edit/joaaAQPJLPy26Do4tA3k

Please try to scroll down and up and you will see the empty space on the top of the page.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 5
  • Comments: 22 (6 by maintainers)

Most upvoted comments

@borNfreee sure, here’s a short write-up first:

The reason why the cells are disappearing is that the overscan is not applied to top-side rows when scrolling forwards. In line defaultOverscanIndicesGetter.js:25 you can see the overscan start index is set to 0 or startIndex (the first visible row determined by some other code), whichever is higher. The problem is the first visible row index value is incorrect if padding is added - some rows before that are still in view, so you need to take that into account and offset the overscanStartIndex by the value of however many rows your padding could fit, so Math.ceil(PADDING / ROW_HEIGHT) or something.

My padding is minuscule so I ended up just subtracting 1 from startIndex in the aforementioned line, but you can reuse the logic for backwards scrolling and subtract the overscanCellsCount argument as seen in line defaultOverscanIndicesGetter.js:33 instead, so that it uses the overscan values supplied via props.

export default function defaultOverscanIndicesGetter({
  cellCount,
  overscanCellsCount,
  scrollDirection,
  startIndex,
  stopIndex,
}: OverscanIndicesGetterParams): OverscanIndices {
  if (scrollDirection === SCROLL_DIRECTION_FORWARD) {
    return {
      // while scrolling forward, apply the overscan to top-side rows as well
      overscanStartIndex: Math.max(0, startIndex - overscanCellsCount), 
      overscanStopIndex: Math.min(
        cellCount - 1,
        stopIndex + overscanCellsCount,
      ),
    };
  } else {
    return {
      overscanStartIndex: Math.max(0, startIndex - overscanCellsCount),
      overscanStopIndex: Math.min(cellCount - 1, stopIndex),
    };
  }
}

This theoretically might need additional tweaking (or higher overscan values) for variable-height rows, but for fixed-height rows it works like a charm.

Guys, JFYI: I didn’t manage to fix this bug, but as a workaround - you can have two separate tables

  1. the first is for header
  2. the second is for data (with disableHeader=true)

It works and fixes this ugly bug, but as I said still a workaround. Please seee renderFakeTableAsAHeader and a complete example

private renderFakeTableAsAHeader(): JSX.Element {
    return (
        <AutoSizer disableHeight={true}>
            {({width, height}) => (
                <Table
                    autoHeight={true}
                    width={width}
                    height={height}
                    headerHeight={HEADER_HEIGHT}
                    rowHeight={this.props.cache.rowHeight}
                    rowCount={0}
                    rowGetter={() => {throw new Error('Logic Error'); }}
                >
                    {this.renderExtraColumns()}
                </Table>
            )}
        </AutoSizer>
    );
}

public render(): JSX.Element {
    return (
        <div>
            {this.renderFakeTableAsAHeader()}
            <WindowScroller scrollElement={scrollElement}>
                {({height, isScrolling, scrollTop}) => (
                    <AutoSizer disableHeight={true}>
                        {({width}) => (
                            <Table
                                autoHeight={true}
                                isScrolling={isScrolling}
                                scrollTop={scrollTop}
                                width={width}
                                height={height}
                                headerHeight={0}
                                disableHeader={true}
                                rowHeight={this.props.cache.rowHeight}
                                rowCount={vehicles.length}
                                rowGetter={({index}) => vehicles[index]}
                                noRowsRenderer={this.noRowsRenderer}
                                ref={(element) => {
                                    this.tableElement = element;
                                }}
                                deferredMeasurementCache={this.props.cache}
                            >
                        )}
                    </AutoSizer>
                )}
            </WindowScroller>
        </div>
    );
}

Table isn’t really compatible with WindowScroller because of the header. If you’d like to dig in and find a way to make the 2 play nicely, I’ll review a PR. I don’t think I’m going to tackle it though due to limited time.

Running into this problem under similar circumstance to that of the original post, but I don’t think this is just a table problem. You can easily replicate the problem by going to https://bvaughn.github.io/react-virtualized/#/components/WindowScroller and editing the section of header to have extra height (min-height: 200px). Then scroll down and you’ll see the list getting clipped.

It seems to me that the actual virtual window is shifting in the event that there is a large header.

Increased header height: image

List clipping: image

if user resize the browser the column header are overlapped. in other words, the horizontal scroll bar doesn’t get appeared.

Table doesn’t really support horizontal scrolling. You could try putting it in a container that scrolls though.

Would you please advice what can I do?

I suggest using Stack Overflow with a #react-virtualized tag or asking in the react-virtualized Slack channel.

Using container does help. Also I managed to make use of flexShrink and set it to zero and that solved the problem. Thanks for your help. I’ll ask my question in Stack Overflow from now on.

Table isn’t really compatible with WindowScroller because of the header.

The documentation at https://github.com/bvaughn/react-virtualized/blob/master/docs/WindowScroller.md says

[… WindowScroller] should be used with Table or List only.