import { GraphQLEdgesInput } from '@ts/types/graphql/generics'; import { EdgesResponse, Search } from '@ts/types/graphql/queries'; import useSWRInfinite, { SWRInfiniteKeyLoader } from 'swr/infinite'; export type UsePaginationProps = { /** * The initial data. */ fallbackData: EdgesResponse[]; /** * A function to fetch more data. */ fetcher: (props: GraphQLEdgesInput & Search) => Promise>; /** * The number of results per page. */ perPage: number; /** * An optional search string. */ search?: string; }; export type UsePaginationReturn = { /** * The data from the API. */ data?: EdgesResponse[]; /** * An error thrown by fetcher. */ error: any; /** * Determine if there's more data to fetch. */ hasNextPage?: boolean; /** * Determine if the initial data is loading. */ isLoadingInitialData: boolean; /** * Determine if more data is currently loading. */ isLoadingMore?: boolean; /** * Determine if the data is refreshing. */ isRefreshing?: boolean; /** * Determine if there's a request or revalidation loading. */ isValidating: boolean; /** * Set the number of pages that need to be fetched. */ setSize: ( size: number | ((_size: number) => number) ) => Promise[] | undefined>; }; /** * Handle data fetching with pagination. * * This hook is a wrapper of `useSWRInfinite` hook. * * @param {UsePaginationProps} props - The pagination configuration. * @returns {UsePaginationReturn} An object with pagination data and helpers. */ const usePagination = ({ fallbackData, fetcher, perPage, search, }: UsePaginationProps): UsePaginationReturn => { const getKey: SWRInfiniteKeyLoader = ( pageIndex: number, previousData: EdgesResponse ): (GraphQLEdgesInput & Search) | null => { // Reached the end. if (previousData && !previousData.edges.length) return null; // Fetch data using this parameters. return pageIndex === 0 ? { first: perPage, search } : { first: perPage, after: previousData.pageInfo.endCursor, search, }; }; const { data, error, isValidating, size, setSize } = useSWRInfinite( getKey, fetcher, { fallbackData } ); const isLoadingInitialData = !data && !error; const isLoadingMore = isLoadingInitialData || (size > 0 && data && typeof data[size - 1] === 'undefined'); const isRefreshing = isValidating && data && data.length === size; const hasNextPage = data && data.length > 0 && data[data.length - 1].pageInfo.hasNextPage; return { data, error, hasNextPage, isLoadingInitialData, isLoadingMore, isRefreshing, isValidating, setSize, }; }; export default usePagination;