import { useState, useCallback, useRef, useEffect } from "react"; import type { PaginatedResponse } from "@/types"; interface UsePaginatedQueryOptions { fetchFn: (cursor?: string) => Promise>; } interface UsePaginatedQueryResult { items: T[]; loading: boolean; error: string | null; hasMore: boolean; loadMore: () => Promise; refresh: () => Promise; } /** * Hook for paginated list fetching with infinite scroll support. */ export function usePaginatedQuery({ fetchFn, }: UsePaginatedQueryOptions): UsePaginatedQueryResult { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [nextCursor, setNextCursor] = useState(null); const loadingRef = useRef(false); const refresh = useCallback(async () => { setLoading(true); setError(null); try { const response = await fetchFn(); setItems(response.results); setNextCursor(response.next); } catch (err) { setError(err instanceof Error ? err.message : "Failed to fetch data"); } finally { setLoading(false); } }, [fetchFn]); const loadMore = useCallback(async () => { if (!nextCursor || loadingRef.current) return; loadingRef.current = true; try { const response = await fetchFn(nextCursor); setItems((prev) => [...prev, ...response.results]); setNextCursor(response.next); } catch (err) { setError(err instanceof Error ? err.message : "Failed to load more"); } finally { loadingRef.current = false; } }, [nextCursor, fetchFn]); useEffect(() => { refresh(); }, [refresh]); return { items, loading, error, hasMore: nextCursor !== null, loadMore, refresh, }; }