mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site): fix agents sidebar not loading all pages when sentinel stays visible (#23613)
This commit is contained in:
@@ -258,6 +258,50 @@ describe("AgentsSidebar load-more behavior", () => {
|
||||
expect(onLoadMore).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("recreates the observer when isFetchingNextPage transitions to false so visible sentinels re-trigger", () => {
|
||||
const onLoadMore = vi.fn();
|
||||
const { rerender } = render(
|
||||
<Wrapper>
|
||||
<AgentsSidebar
|
||||
{...defaultProps}
|
||||
hasNextPage
|
||||
onLoadMore={onLoadMore}
|
||||
isFetchingNextPage={false}
|
||||
/>
|
||||
</Wrapper>,
|
||||
);
|
||||
|
||||
const countAfterMount = observeCount;
|
||||
expect(countAfterMount).toBe(1);
|
||||
|
||||
// Start fetching — observer is torn down.
|
||||
rerender(
|
||||
<Wrapper>
|
||||
<AgentsSidebar
|
||||
{...defaultProps}
|
||||
hasNextPage
|
||||
onLoadMore={onLoadMore}
|
||||
isFetchingNextPage={true}
|
||||
/>
|
||||
</Wrapper>,
|
||||
);
|
||||
|
||||
// Fetch completes — a fresh observer is created, firing
|
||||
// an initial entry that detects the still-visible sentinel.
|
||||
rerender(
|
||||
<Wrapper>
|
||||
<AgentsSidebar
|
||||
{...defaultProps}
|
||||
hasNextPage
|
||||
onLoadMore={onLoadMore}
|
||||
isFetchingNextPage={false}
|
||||
/>
|
||||
</Wrapper>,
|
||||
);
|
||||
|
||||
expect(observeCount).toBe(countAfterMount + 1);
|
||||
});
|
||||
|
||||
it("does NOT render the sentinel when hasNextPage is false", () => {
|
||||
const onLoadMore = vi.fn();
|
||||
render(
|
||||
|
||||
@@ -1131,27 +1131,29 @@ const LoadMoreSentinel: FC<{
|
||||
}> = ({ onLoadMore, isFetchingNextPage }) => {
|
||||
const sentinelRef = useRef<HTMLDivElement>(null);
|
||||
const onLoadMoreRef = useRef(onLoadMore);
|
||||
const isFetchingNextPageRef = useRef(isFetchingNextPage);
|
||||
|
||||
// Keep refs in sync with the latest prop values so the
|
||||
// observer callback always reads current state without
|
||||
// needing to tear down and re-create the observer.
|
||||
// Keep the callback ref in sync so the observer closure
|
||||
// always calls the latest onLoadMore without needing to
|
||||
// tear down and re-create the observer.
|
||||
useEffect(() => {
|
||||
onLoadMoreRef.current = onLoadMore;
|
||||
isFetchingNextPageRef.current = isFetchingNextPage;
|
||||
}, [onLoadMore, isFetchingNextPage]);
|
||||
}, [onLoadMore]);
|
||||
|
||||
useEffect(() => {
|
||||
// Don't observe while a fetch is in progress. When the
|
||||
// fetch completes this effect re-runs, creating a fresh
|
||||
// observer whose initial entry detects the sentinel if
|
||||
// it's still visible — fixing the case where loaded items
|
||||
// don't push the sentinel out of view and the previous
|
||||
// observer never re-fires.
|
||||
if (isFetchingNextPage) return;
|
||||
|
||||
const el = sentinelRef.current;
|
||||
if (!el) return;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (
|
||||
entries[0]?.isIntersecting &&
|
||||
!isFetchingNextPageRef.current &&
|
||||
onLoadMoreRef.current
|
||||
) {
|
||||
if (entries[0]?.isIntersecting && onLoadMoreRef.current) {
|
||||
onLoadMoreRef.current();
|
||||
}
|
||||
},
|
||||
@@ -1159,7 +1161,7 @@ const LoadMoreSentinel: FC<{
|
||||
);
|
||||
observer.observe(el);
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
}, [isFetchingNextPage]);
|
||||
|
||||
return (
|
||||
<div ref={sentinelRef} className="flex items-center justify-center py-2">
|
||||
|
||||
Reference in New Issue
Block a user