bvaughn / react-window

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

[InfiniteLoader] loadMoreItems is not always called when using a loading indicator as an list item. #733

Open radiol00 opened 10 months ago

radiol00 commented 10 months ago

loadMoreItems callback in InfiniteLoader is fired only when child arg onItemsRendered is called. The problem is in the FixedSizeList onItemsRendered is called only when the visible/overscanned indicies change and it's not always the case when the loading indicator is displayed as a last item.

Suppose we have 3 items in FixedSizeList:

Visible/overscanned indicies are:

{overscanStartIndex: 0, overscanStopIndex: 2, visibleStartIndex: 0, visibleStopIndex: 2}

Now if I somehow delete the last item, maybe because the filters of the list changed and I'm fetching new data, and replace it with a loading indicator.

The indicies are still:

{overscanStartIndex: 0, overscanStopIndex: 2, visibleStartIndex: 0, visibleStopIndex: 2}

Thus the onItemsRendered in the FixedSizeList (and then in InfiniteLoader) is not called and loadMoreItems is not called leaving me with the loading indicator visible at all times, even when the data changed.

radiol00 commented 10 months ago

I'm not sure if this is bulletproof solution but what I did to overcome this issue is I added some "inbetween" time when updating my "hasNextPage" variable. Just so there is a noticeable change of indices for the FixedSizeList to fire all callbacks.

  const [pageLoading, setPageLoading] = useState(
    pagination?.hasNextPage ?? false
  );

  useEffect(() => {
    const value = pagination?.hasNextPage ?? false;

    if (pageLoading) {
      setTimeout(() => {
        setPageLoading(value);
      }, 100);
    } else {
      setPageLoading(value);
    }
  }, [pagination?.hasNextPage]);

  // Use pageLoading instead of pagination.hasNextPage
  [...]
  <InfiniteLoader
      isItemLoaded={
      pagination
        ? (index) => !pageLoading || index < data.length
        : () => true
    }
  >
  [...}
  <FixedSizeList
     itemCount={data.length + (pageLoading ? 1 : 0)}
   >
  [...]
  </FixedSizeList>
  </InfiniteLoader>