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?