added infinite scroll functionality

This commit is contained in:
Nate Kelley 2025-10-01 16:38:11 -06:00
parent 9a9b5fb674
commit 02ce6d6b3a
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
2 changed files with 14 additions and 42 deletions

View File

@ -1,13 +1,9 @@
import type { SearchTextResponse } from '@buster/server-shared/search'; import type { SearchTextData, SearchTextResponse } from '@buster/server-shared/search';
import { import { keepPreviousData, type UseQueryOptions, useQuery } from '@tanstack/react-query';
keepPreviousData,
type UseQueryOptions,
useInfiniteQuery,
useQuery,
} from '@tanstack/react-query';
import { useState } from 'react'; import { useState } from 'react';
import type { ApiError } from '@/api/errors'; import type { ApiError } from '@/api/errors';
import { getSearchResultInfinite, searchQueryKeys } from '@/api/query_keys/search'; import { getSearchResultInfinite, searchQueryKeys } from '@/api/query_keys/search';
import { useInfiniteScroll } from '@/api/query-helpers';
import { search } from './requests'; import { search } from './requests';
export const useSearch = <T = SearchTextResponse>( export const useSearch = <T = SearchTextResponse>(
@ -31,17 +27,10 @@ export const useSearchInfinite = (
) => { ) => {
const [searchQuery, setSearchQuery] = useState<string>(''); const [searchQuery, setSearchQuery] = useState<string>('');
const queryResult = useInfiniteQuery({ const queryResult = useInfiniteScroll<SearchTextData>({
queryKey: ['search', 'results', 'infinite', params] as const, queryKey: ['search', 'results', 'infinite', params] as const,
staleTime: 1000 * 30, // 30 seconds, staleTime: 1000 * 30, // 30 seconds
queryFn: ({ pageParam = 1 }) => search({ query: searchQuery, page: pageParam, ...params }), queryFn: ({ pageParam = 1 }) => search({ query: searchQuery, page: pageParam, ...params }),
getNextPageParam: (lastPage) => {
if (!lastPage.pagination.has_more) {
return undefined;
}
return lastPage.pagination.page + 1;
},
initialPageParam: 1,
}); });
return { ...queryResult, setSearchQuery, searchQuery }; return { ...queryResult, setSearchQuery, searchQuery };

View File

@ -1,36 +1,19 @@
import { useInfiniteQuery } from '@tanstack/react-query';
import { createFileRoute } from '@tanstack/react-router'; import { createFileRoute } from '@tanstack/react-router';
import { useEffect, useRef } from 'react'; import { useSearchInfinite } from '@/api/buster_rest/search';
import { search, useSearchInfinite } from '@/api/buster_rest/search';
export const Route = createFileRoute('/app/_app/test-pagination')({ export const Route = createFileRoute('/app/_app/test-pagination')({
component: RouteComponent, component: RouteComponent,
}); });
function RouteComponent() { function RouteComponent() {
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = useSearchInfinite(); const {
const scrollContainerRef = useRef<HTMLDivElement>(null); scrollContainerRef,
allResults,
// Combine all pages into a single array of results hasNextPage,
const allResults = data?.pages.flatMap((page) => page.data) ?? []; isFetchingNextPage,
isLoading,
useEffect(() => { fetchNextPage,
const container = scrollContainerRef.current; } = useSearchInfinite();
if (!container) return;
const handleScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = container;
// Trigger when user is within 100px of the bottom
if (scrollHeight - scrollTop - clientHeight < 100) {
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}
};
container.addEventListener('scroll', handleScroll);
return () => container.removeEventListener('scroll', handleScroll);
}, [hasNextPage, isFetchingNextPage, fetchNextPage]);
return ( return (
<div <div