bvaughn / react-window

React components for efficiently rendering large lists and tabular data
https://react-window.now.sh/
MIT License
15.91k stars 787 forks source link

How to get content aware non-overlapping rows in VariableSizeList in react-window? #593

Closed gregsifr closed 2 months ago

gregsifr commented 3 years ago

In the official documentation example the itemSize is not content aware:

const rowSizes = new Array(1000)
  .fill(true)
  .map(() => 25 + Math.round(Math.random() * 50));

const getItemSize = index => rowSizes[index];

I am trying to figure out how to do it using the below code (which is based on the official react-window-infinite-loader example although I've refactored it to be hook based).

The below code has VariableSizeList, AutoSizer and InfiniteLoader which display data from an API.

import React, { useState } from "react";
import { VariableSizeList as List } from "react-window";
import InfiniteLoader from "react-window-infinite-loader";
import AutoSizer from "react-virtualized-auto-sizer";
import axios from "axios";
import { LoremIpsum } from "react-lorem-ipsum";

export default function ExampleWrapper() {
  const [hasNextPage, setHasNextPage] = useState(true);
  const [isNextPageLoading, setIsNextPageLoading] = useState(false);
  const [items, setItems] = useState([]);

  const _loadNextPage = (...args) => {
    setIsNextPageLoading(true);
    setHasNextPage(items.length < 100);

    axios
      .get(
        `https://randomuser.me/api/?results=5&page=1&inc=name,gender,email,nat&noinfo`
      )
      .then((response) => {
        // console.log("response", response.data.results);
        setItems([...items, ...response.data.results]);
        // console.log("items", items);
        setIsNextPageLoading(false);
      })
      .catch((error) => {
        setIsNextPageLoading(false);
        console.error("Error:", error);
      });
    setIsNextPageLoading(false);
  };

  // If there are more items to be loaded then add an extra row to hold a loading indicator.
  const itemCount = hasNextPage ? items.length + 1 : items.length;

  // Only load 1 page of items at a time.
  // Pass an empty callback to InfiniteLoader in case it asks us to load more than once.
  const loadMoreItems = isNextPageLoading ? () => {} : _loadNextPage;

  // Every row is loaded except for our loading indicator row.
  const isItemLoaded = (index) => !hasNextPage || index < items.length;

  // Render an item or a loading indicator.
  const Item = ({ index, style }) => {
    // console.log(items[index].email);
    const content = !isItemLoaded(index) ? (
      "Loading..."
    ) : (
      <div style={{ display: "flex" }}>
        <div>{index}</div>
        <div>{items[index].email}</div>
        <div>
          <LoremIpsum random={true} />
        </div>
      </div>
    );

    return (
      <div key={index} style={style}>
        {content}
      </div>
    );
  };

  return (
    <div style={{ height: "100vh" }}>
      <AutoSizer>
        {({ height, width }) => (
          <InfiniteLoader
            isItemLoaded={isItemLoaded}
            itemCount={itemCount}
            loadMoreItems={loadMoreItems}
          >
            {({ onItemsRendered, ref }) => (
              <List
                className="List"
                height={height}
                itemCount={itemCount}
                itemSize={() => 100}
                onItemsRendered={onItemsRendered}
                ref={ref}
                width={width}
              >
                {Item}
              </List>
            )}
          </InfiniteLoader>
        )}
      </AutoSizer>
    </div>
  );
}

Here is the above code in a working codesandbox link.

How to get the rows to not overlap?

I found one code example which seems to result in overlapping rows: https://codesandbox.io/s/yw4ok6l69z

And another example which seems complex and I have no idea how it works or how to adapt it to the above example: https://codesandbox.io/s/bugreact-window-dynamic-size-list-djegq. Seems like this example uses DynamicSizeList which is not officially released yet?