react-window-infinite-scroll is a infinite scroll library specifically designed for react-window.
github link: https://github.com/dlguswo333/react-window-infinite-scroll npm link: https://www.npmjs.com/package/react-window-infinite-scroll
react-window-infinite-scroll is to reliably load more items even when the following conditions hold:
-
loadMoreItems
callback might cancel fetching more items. - There are not enough initial items to fill the scrollable element, thereby no way to induce
onItemsRendered
callback. - Not only do you need to scroll infinitely to the end, but also to the top.
type Props = {
/** Return whether an item at the index has been loaded. */
isItemLoaded: (index: number) => boolean;
/**
* Callback to load more items.
* Receives a parameter `'start'` or `'end'` to load items at the start or end.
*/
loadMoreItems: (direction: Direction) => Promise<unknown> | unknown;
/**
* Callback to be called when items have been rendered.
* This prop is optional.
*/
onItemsRendered?: OnItemsRendered;
/** Returns the number of items. Can be arbitrary large if the total size is unknown. */
itemCount: number;
/**
* Threshold value to load more items on items rendered.
* When 1st ~ *threshold*th first item at either end is visible, `loadMoreItems` will be called.
* Setting the value `0` or smaller may disable calling `loadMoreItems` on items rendered.
*/
threshold: number;
/**
* offset value (in `px`) for smooth infinite scrolling.
* Recommends value equal to minimum height of items or higher.
*/
scrollOffset: number;
/** children that receives `onItemsRendered` props and returns `ReactNode`. */
children: ({onItemsRendered}: {onItemsRendered: OnItemsRendered}) => ReactNode;
/** `ref` to the outer continer element. Passing the HTML element directly is also permitted. */
outerRef: React.RefObject<HTMLElement> | HTMLElement;
/** *Actual* data that needs to be virtually scrolled. */
data: unknown[];
}
Here is a simple example where you can do infinite scroll with react-window-infinite-scroll.
<InfiniteScroll
data={data}
loadMoreItems={loadMoreItems}
isItemLoaded={isItemsLoaded}
itemCount={itemCount}
threshold={1}
outerRef={outerRef}
scrollOffset={30}
>
{({onItemsRendered}) => <FixedSizeList
height={300}
itemCount={itemCount}
width='100%'
onItemsRendered={onItemsRendered}
itemSize={30}
outerRef={outerRef}
className='Outer'
>
{Row}
</FixedSizeList>}
</InfiniteScroll>
You need to have react 17 or higher because we utilize new JSX transform.
In some cases, where loadMoreItems
API author does not like it to be called too frequently,
he/she might add a conditional statement.
The reasons for this may vary, from improving browser performance to preventing server overload.
The following is an example code where loadMoreItems
might cancel fetching more items.
const loadMoreItems = () => {
if (isFetchingAlready) {
return;
}
isFetchingAlready = true;
fetchItems();
isFetchingAlready = false;
}
In this case, some infinite scrolling packages do not work due to the blocking.
However, react-window-infinite-scroll tries its best to mitigate the problem,
by subscribing to both data changes and item rendered events.
When fetching is complete, the conditional value will be reset,
and then the data changes or item render events will be fired afterwards.
scrollOffset
shall not be optional since it is crucial to load more items.
With it being optional, infinite scroll could have not loaded more items
when the outer element's height and the inner element's height are almost equal.
thus onItemsRendered
is not likely to be called,
and also scrollOffset
is too small to call loadMoreItems
.
That is why we recommend you set the value as big as the minimum height of items being rendered.
This is to load more items reliably.
When many rows are loaded at once,
data change events would most likely not to load more items
since the scrollbar is not near the bottom or the top.
Later on, when you scroll the component, onItemsRendered
will fire and load more items in that case.
In another case, where you have as many rows as the infinite scrolling component is barely filled,
onItemsRendered
would most likely not to be called,
(Especially when you have big overscanCount
prop value of react-window
.)
even though you have tiny (too tiny!) scrollable height.
Data change event will help load more items in that case.
There maybe several bugs and issues with the package.
Some possible issues with the component are that it may call loadMoreItems
too many times,
or that it may not call loadMoreItems
.
One feasible reason why it calls loadMoreItems
too many times is that
loadMoreItems
does not await until data
changes and return.
This is because react-window-infinite-scroll tries to prevent excessive loadMoreItems
calls for performance,
by waiting for loadMoreItems
to finish one at a time.
Even if loadMoreItems
finishes earlier than data
changes,
the package might not notice it and call loadMoreItems
and recall once more when data
changes.
If loadMoreItems
has something to do with promise
s,
wait for the responses and update the data
. Please do not exit right after making API calls
since it can lead to undefined behaviors.
// Please don't do this ✖️
const loadMoreItems = () => {
fetchFromRemote().then((response) => {
setData(data => [...data, response]);
});
};
// Or this ✖️
const loadMoreItems = () => {
fetchFromRemote((response) => {
setData(data => [...data, response]);
});
};
// Please do this ✔️
const loadMoreItems = async () => {
const response = await fetchFromRemote();
setData(data => [...data, response]);
};
- Update dependencies
- Revert deleted force rerendering code (Refer to v0.0.5).
- Force rerender only if the data has been changed.
- Remove force rerendering code as it may fall into an infinite rerendering loop.
- Improve
loadMoreItems
may not execute properly.- Fix
loadMoreItems
may not work when it loads items synchronously. - Fix
loadMoreItems
may not work if rerendering runs before the returned promise resolves. - Fix items may not be loaded.
- Fix
- Add playwright test codes
- Fix an error where
outerRef
hasnull
orundefined
value.
- Allow
outerRef
prop to haveHTMLElement
type - Improve README
npm run deploy
Do not run npm publish
in root directory or it might publish monorepo project.
We have private:true
in root package.json to prevent the situation.
test.Dockerfile
builds an docker image for test.
The dockerfile will run the test cases while building. You don't need the output image.
The recommended shell command to test with docker is the following:
docker build --output type=tar,dest=/dev/null -f ./package/test.Dockerfile --network host ./package