import {useCallback, useMemo} from 'react' import {ShopActionResult} from '@shopify/shop-minis-platform/actions' import {useInfiniteQuery} from '@tanstack/react-query' import {DataHookFetchPolicy} from '../../types' import {useShopMinisQueryClient} from './queryClient' /** * Helper to use React Query with Shop Actions (paginated) * Replaces useShopActionsPaginatedDataFetching * * @example * ```ts * // Example: Fetching saved products * const { getSavedProducts } = useShopActions() * const { data, loading, error, hasNextPage, fetchMore, refetch } = * useShopActionInfiniteQuery( * ['savedProducts', { includeSensitive }], // Query key * getSavedProducts, // Shop Action * { includeSensitive }, // Params (excludes 'after') * { skip: false } // Options * ) * // data will be flattened array of products from all pages * ``` */ export function useShopActionInfiniteQuery< TData, TParams extends {after?: string; fetchPolicy?: DataHookFetchPolicy}, >( queryKey: unknown[], action: (params: TParams) => Promise< ShopActionResult<{ data: TData pageInfo: {hasNextPage: boolean; endCursor: string | null} }> >, params: Omit, options?: { skip?: boolean } ) { const {skip = false} = options ?? {} // Always use our SDK's QueryClient for isolation const queryClient = useShopMinisQueryClient() interface PageData { data: TData pageInfo: {hasNextPage: boolean; endCursor: string | null} } const { data, fetchNextPage, hasNextPage, isLoading, error, refetch: reactQueryRefetch, } = useInfiniteQuery< PageData, Error, {pages: PageData[]}, unknown[], string | undefined >( { queryKey, queryFn: async ({pageParam}: {pageParam: string | undefined}) => { const result = await action({ ...params, after: pageParam, } as TParams) if (!result.ok) { throw result.error } return result.data }, getNextPageParam: (lastPage: PageData) => lastPage.pageInfo.hasNextPage ? lastPage.pageInfo.endCursor : undefined, initialPageParam: undefined as string | undefined, enabled: !skip, // Caching disabled by default (handled by Apollo) // fetchPolicy param is passed through to the action (Apollo layer) }, queryClient ) // Flatten paginated data // For paginated queries, we expect TData to be an array type // Each page.data is an array that we concatenate together const flattenedData = useMemo(() => { if (!data?.pages || data.pages.length === 0) return null // If first page data is null/undefined, return null const firstPageData = data.pages[0].data if (firstPageData === null || firstPageData === undefined) return null // If data is array type, flatten all pages if (Array.isArray(firstPageData)) { return data.pages.flatMap((page: PageData) => page.data as any) as TData } // If data is not an array, just return the first page's data // (Though in practice, all Shop Minis paginated queries return arrays) return firstPageData as TData }, [data?.pages]) // Wrap React Query functions to match expected API const fetchMore = useCallback(async () => { await fetchNextPage() }, [fetchNextPage]) const refetch = useCallback(async () => { await reactQueryRefetch() }, [reactQueryRefetch]) return { data: flattenedData, loading: isLoading, error: error as Error | null, hasNextPage: hasNextPage ?? false, fetchMore, refetch, } }