Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions packages/hooks/src/useInfiniteScroll/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -532,4 +532,84 @@ describe('useInfiniteScroll', () => {
scrollHeightSpy.mockRestore();
clientHeightSpy.mockRestore();
});

test('should auto loadMore after initial load when container is not full, and second request receives the latest finalData', async () => {
setTargetInfo('scrollTop', 0);
const scrollHeightSpy = vi.spyOn(targetEl, 'scrollHeight', 'get').mockImplementation(() => 50);
const clientHeightSpy = vi.spyOn(targetEl, 'clientHeight', 'get').mockImplementation(() => 300);

let capturedLastData: any;
const service = vi.fn(async (lastData?: any) => {
capturedLastData = lastData;
await sleep(1000);
if (!lastData) {
return { list: [1, 2, 3], nextId: 1 };
}
return { list: [4, 5, 6] };
});

const { result } = setup(service, {
target: targetEl,
isNoMore: (d) => d?.nextId === undefined,
});

// Wait for initial load to complete
await act(async () => {
vi.advanceTimersByTime(1000);
});

// Container is not full, so scrollMethod should have auto-triggered loadMore
expect(result.current.loadingMore).toBe(true);
// The second call must receive the up-to-date finalData (not the stale closure value)
expect(capturedLastData).toMatchObject({ list: [1, 2, 3], nextId: 1 });

await act(async () => {
vi.advanceTimersByTime(1000);
});
expect(result.current.data?.list).toMatchObject([1, 2, 3, 4, 5, 6]);
Comment thread
bowencool marked this conversation as resolved.
Outdated
expect(result.current.noMore).toBe(true);

scrollHeightSpy.mockRestore();
clientHeightSpy.mockRestore();
});

test('mutate should not trigger extra loadMore', async () => {
setTargetInfo('scrollTop', 0);
const scrollHeightSpy = vi.spyOn(targetEl, 'scrollHeight', 'get').mockImplementation(() => 50);
const clientHeightSpy = vi.spyOn(targetEl, 'clientHeight', 'get').mockImplementation(() => 300);

const service = vi.fn(mockRequest);
const { result } = setup(service, {
target: targetEl,
isNoMore: (d) => d?.nextId === undefined,
});

// Wait for initial load to complete; this auto-triggers a second load
await act(async () => {
vi.advanceTimersByTime(1000);
});
// Wait for the auto-triggered second load to complete
await act(async () => {
vi.advanceTimersByTime(1000);
});
expect(result.current.noMore).toBe(true);

const callCountAfterLoad = service.mock.calls.length;

// Mutate with data that makes noMore false, so loadMore *would* fire if scrollMethod ran
act(() => {
result.current.mutate({ list: [10, 20], nextId: 1 });
});

// Wait for any effects to run
await act(async () => {
vi.advanceTimersByTime(100);
});

// mutate should NOT trigger additional requests via scrollMethod
expect(service).toBeCalledTimes(callCountAfterLoad);

scrollHeightSpy.mockRestore();
clientHeightSpy.mockRestore();
});
});
15 changes: 12 additions & 3 deletions packages/hooks/src/useInfiniteScroll/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ const useInfiniteScroll = <TData extends Data>(
const lastScrollTop = useRef<number>(undefined);
// scrollBottom is used to record the distance from the bottom of the scroll bar
const scrollBottom = useRef<number>(0);
// flag: set in onSuccess (bottom direction only) to trigger scrollMethod via effect
const shouldScrollCheckRef = useRef(false);

const noMore = useMemo(() => {
if (!isNoMore) {
Expand Down Expand Up @@ -76,13 +78,13 @@ const useInfiniteScroll = <TData extends Data>(
const scrollHeight = getScrollHeight(el);
(el as Element).scrollTo(0, scrollHeight - scrollBottom.current);
}
} else {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
scrollMethod();
}
});
Comment thread
bowencool marked this conversation as resolved.
});

if (!isScrollToTop) {
shouldScrollCheckRef.current = true;
}
onSuccess?.(d.currentData);
},
onError: (e) => onError?.(e),
Expand Down Expand Up @@ -145,6 +147,13 @@ const useInfiniteScroll = <TData extends Data>(
loadMore();
}
};
useUpdateEffect(() => {
if (!shouldScrollCheckRef.current) {
return;
}
shouldScrollCheckRef.current = false;
scrollMethod();
Comment thread
bowencool marked this conversation as resolved.
}, [finalData]);
Comment thread
bowencool marked this conversation as resolved.
Comment thread
bowencool marked this conversation as resolved.

useEventListener(
'scroll',
Expand Down
Loading