bvaughn / react-virtualized

React components for efficiently rendering large lists and tabular data
http://bvaughn.github.io/react-virtualized/
MIT License
26.12k stars 3.06k forks source link

Grid Row Heights not computing correct height when using cell measurer to render a placeholder #1707

Open AaronMcCloskey opened 2 years ago

AaronMcCloskey commented 2 years ago

Bug Report

CodeSandBox Example

CodeSandBox FullScreen

Screenshot 2021-11-02 at 11 14 35

What is the current behavior?

I am using the Grid component inside a ColumnSizer with AutoSizer and WindowScroller inside of an InfiniteLoader

For each GridItem I am using a CellMeasurer and cache in the cellRenderer attribute.

In the function for cellRenderer I am checking for isVisible and if it's not visible, then render a loader component, wrapped in a CellMeasurer.

If isVisible resolves are true then render the cell contents.

const cache = useMemo(
  () =>
    new CellMeasurerCache({
      fixedWidth: true,
    }),
  [],
);

const renderItem = () => {
  return (
    <div>
      <h1 style={{ padding: '400px 10px', background: 'orange' }}>rendered</h1>
    </div>
  );
};

const renderLoadingItem = () => (
  <div>
    <h1 style={{ padding: '300px 10px', background: 'purple' }}>LOADING!!!!!</h1>
  </div>
);

const cellRenderer = (
  { rowIndex, columnIndex, style, key, parent, isVisible }: GridCellProps,
  columnCount: number,
) => {
  if (renderLoadingItem && (isLoading || !isVisible))
    return (
      <CellMeasurer key={key} cache={cache} parent={parent} columnIndex={columnIndex} rowIndex={rowIndex}>
        {({ registerChild }: any) => (
          <div
            ref={registerChild}
            style={{
              ...style,
              overflow: 'visible',
            }}
            key={key}
          >
            {renderLoadingItem()}
          </div>
        )}
      </CellMeasurer>
    );

  const index = rowIndex * columnCount + columnIndex;
  const gridItem = items?.[index];

  if (!gridItem || !renderItem) return null;

  return (
    <CellMeasurer key={key} cache={cache} parent={parent} columnIndex={columnIndex} rowIndex={rowIndex}>
      {({ registerChild }: any) => (
        <div
          ref={registerChild}
          style={{
            ...style,
            background: 'green',
            overflow: 'visible',
          }}
          key={key}
        >
          {renderItem(gridItem)}
        </div>
      )}
    </CellMeasurer>
  );
};

<InfiniteLoader
  isRowLoaded={isRowLoaded}
  loadMoreRows={loadMoreRows}
  rowCount={isLoading ? overscanRowCount : totalResults}
  threshold={1}
>
  ({ onRowsRendered }: InfiniteLoaderChildProps) => (
  <WindowScroller>
    {({ height, scrollTop, onChildScroll }) => (
      <AutoSizer disableHeight onResize={onResize}>
        {({ width }) => {
          const columnCount = Math.max(Math.floor(width / (defaultWidth || width)), 1);
          return (
            <ColumnSizer width={width} columnCount={columnCount}>
              {({ registerChild }) => (
                <Grid
                  autoHeight
                  width={width}
                  height={height}
                  scrollTop={scrollTop}
                  ref={registerChild}
                  overscanRowCount={overscanRowCount}
                  scrollingResetTimeInterval={0}
                  onScroll={onChildScroll}
                  columnWidth={Math.floor(width / columnCount)}
                  columnCount={columnCount}
                  rowCount={Math.ceil((isLoading || !items ? overscanRowCount : items?.length) / columnCount)}
                  rowHeight={cache.rowHeight}
                  cellRenderer={(gridCellProps: GridCellProps) => cellRenderer(gridCellProps, columnCount)}
                  noContentRenderer={noContentRenderer || undefined}
                  onSectionRendered={({
                    rowStartIndex,
                    rowStopIndex,
                    columnStartIndex,
                    columnStopIndex,
                  }: SectionRenderedParams) => {
                    const startIndex = rowStartIndex * columnCount + columnStartIndex;
                    const stopIndex = rowStopIndex * columnCount + columnStopIndex;
                    return onRowsRendered({ startIndex, stopIndex });
                  }}
                />
              )}
            </ColumnSizer>
          );
        }}
      </AutoSizer>
    )}
  </WindowScroller>
)
</InfiniteLoader>

However, the issue is, that the cell contents and the loader component may have different heights, as the content height is dynamic in this instance. So, if the loader height is smaller than the cell contents, the cell contents, when rendered gets cut off unexpectantly.

How can I avoid this? Any help would be greatly appreciated!

What is the expected behavior?

When different content is rendered inside a CellMeasurer, it computes the new height

Which versions of React and react-virtualized, and which browser / OS are affected by this issue? Did this work in previous versions of react-virtualized?

Browser *
OS *
React 17.0.2
React DOM 17.0.2
react-virtualized 9.22.3
edisonLzy commented 1 year ago

Have you fixed it ? i got same issuer