Skip to content

Commit 2da7d04

Browse files
committed
first attempt at refactoring useLoadmore
1 parent 7708b81 commit 2da7d04

File tree

1 file changed

+32
-24
lines changed

1 file changed

+32
-24
lines changed

packages/@react-aria/utils/src/useLoadMore.ts

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -56,44 +56,52 @@ export function useLoadMore(props: LoadMoreProps, ref: RefObject<HTMLElement | n
5656
// this means we could end up calling onLoadMore multiple times if isLoading changes but the collection takes time to update
5757
let collectionAwaitingUpdate = useRef(collection && isLoading);
5858
useLayoutEffect(() => {
59+
if (!ref.current) {
60+
return;
61+
}
62+
5963
// Only update this flag if the collection changes when we aren't loading. Guard against the addition of a loading spinner when a load starts
6064
// which mutates the collection? Alternatively, the user might wipe the collection during load
6165
// If collection isn't provided, update flag
6266
if (collection !== lastCollection.current && !isLoading) {
6367
collectionAwaitingUpdate.current = false;
6468
}
6569

66-
// TODO: if we aren't loading, if the collection has changed, and the height is the same, we should load more
67-
// if we aren't loading, if the collection is the same, and the height is the same, we are either in a case where we are still processing
68-
// the collection and thus don't want to trigger a load or we had items preloaded and need to load more. That means comparing collection to lastCollection is
69-
// insufficient
70-
// might need to wait for height to change?
71-
// TODO: This doesn't quite work if we dont set a rowHeight in the ListLayout, this is because when
72-
// Will also need to test against a case where there are sections being loaded and/or estimated height
73-
// Ideally this layoutEffect would trigger after the collection updates AND the item size has settled
74-
// Previously, Virtualizer passed state from useVirtualizerState into this hook so that we could get a rerender and trigger this effect
75-
// as well as compare previous/last sizes
76-
let shouldLoadMore = onLoadMore
77-
&& !isLoading
78-
// For v3 virtualizer, no collection will be provided to this hook so skip this check. Otherwise we should stop loadMore
79-
// calls in RAC if we are waiting for the collection to be updated from a async load call
80-
&& (!(collection && collectionAwaitingUpdate.current))
81-
&& (ref?.current && ref.current.clientHeight === ref.current.scrollHeight);
70+
let observer = new IntersectionObserver((entries) => {
71+
let allItemsInView = true;
72+
entries.forEach((entry) => {
73+
// TODO this is problematic if the entry is a long row since a part of it will always out of view meaning the intersectionRatio is < 1
74+
if (entry.intersectionRatio < 1) {
75+
allItemsInView = false;
76+
}
77+
});
8278

83-
if (shouldLoadMore) {
84-
onLoadMore?.();
85-
// Only update this flag if a collection has been provided, v3 virtualizer doesn't provide a collection so we don't need
86-
// to use collectionAwaitingUpdate at all.
87-
if (collection !== null && lastCollection.current !== null) {
88-
collectionAwaitingUpdate.current = true;
79+
if (allItemsInView && !isLoading && !(collection && collectionAwaitingUpdate.current) && onLoadMore) {
80+
onLoadMore();
81+
if (collection !== null && lastCollection.current !== null) {
82+
collectionAwaitingUpdate.current = true;
83+
}
8984
}
85+
}, {root: ref.current});
86+
87+
// TODO: right now this is using the Virtualizer's div that wraps the rows, but perhaps should be the items themselves
88+
// This also has various problems because we'll need to figure out what is the proper element to compare the scroll container height to
89+
90+
let lastElement = ref.current.querySelector('[role="presentation"]');
91+
// let lastElement = ref.current.querySelector('[role="presentation"]>[role="presentation"]:last-child');
92+
if (lastElement) {
93+
observer.observe(lastElement);
9094
}
9195

92-
// TODO: only update this when isLoading is false? Might need to guard against the case where loading spinners are added/collection is temporarly wiped/
93-
// loading spinner is removed when loading finishes (this last one we might still need to guard against somehow...). Seems to be ok for now
9496
lastCollection.current = collection;
97+
return () => {
98+
if (observer) {
99+
observer.disconnect();
100+
}
101+
};
95102
}, [isLoading, onLoadMore, props, ref, collection]);
96103

104+
97105
// TODO: maybe this should still just return scroll props?
98106
// Test against case where the ref isn't defined when this is called
99107
// Think this was a problem when trying to attach to the scrollable body of the table in OnLoadMoreTableBodyScroll

0 commit comments

Comments
 (0)