diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-05-13 15:39:55 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-05-13 15:46:05 +0200 |
| commit | dab72bb270ee2ee47a0b472d5e9e240cba7cbf0f (patch) | |
| tree | a64a49a1048eeab1204a9b04923135edd1f259e1 /src/utils/hooks | |
| parent | c5b516e2c933e77b2550fe6becebacb3fbdd30eb (diff) | |
chore: handle blog pagination
Diffstat (limited to 'src/utils/hooks')
| -rw-r--r-- | src/utils/hooks/use-pagination.tsx | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/src/utils/hooks/use-pagination.tsx b/src/utils/hooks/use-pagination.tsx new file mode 100644 index 0000000..1e24b75 --- /dev/null +++ b/src/utils/hooks/use-pagination.tsx @@ -0,0 +1,116 @@ +import { type EdgesResponse, type EdgesVars } from '@services/graphql/api'; +import useSWRInfinite, { SWRInfiniteKeyLoader } from 'swr/infinite'; + +export type UsePaginationProps<T> = { + /** + * The initial data. + */ + fallbackData: EdgesResponse<T>[]; + /** + * A function to fetch more data. + */ + fetcher: (props: EdgesVars) => Promise<EdgesResponse<T>>; + /** + * The number of results per page. + */ + perPage: number; + /** + * An optional search string. + */ + search?: string; +}; + +export type UsePaginationReturn<T> = { + /** + * The data from the API. + */ + data?: EdgesResponse<T>[]; + /** + * 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<EdgesResponse<T>[] | 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 = <T extends object>({ + fallbackData, + fetcher, + perPage, + search, +}: UsePaginationProps<T>): UsePaginationReturn<T> => { + const getKey: SWRInfiniteKeyLoader = ( + pageIndex: number, + previousData: EdgesResponse<T> + ): EdgesVars | 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[data.length - 1].pageInfo.hasNextPage; + + return { + data, + error, + hasNextPage, + isLoadingInitialData, + isLoadingMore, + isRefreshing, + isValidating, + setSize, + }; +}; + +export default usePagination; |
