Skip to content

Commit 26db477

Browse files
committed
feat(storefront-data): add infinite products hook
1 parent acb52d7 commit 26db477

4 files changed

Lines changed: 111 additions & 0 deletions

File tree

libs/storefront-data/src/products/hooks.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
useInfiniteQuery,
23
useQuery,
34
useQueryClient,
45
useSuspenseQuery,
@@ -10,10 +11,12 @@ import { createProductQueryKeys } from "./query-keys"
1011
import { resolvePagination } from "./pagination"
1112
import type {
1213
ProductDetailInputBase,
14+
ProductInfiniteInputBase,
1315
ProductListInputBase,
1416
ProductQueryKeys,
1517
ProductService,
1618
RegionInfo,
19+
UseInfiniteProductsResult,
1720
UseProductsResult,
1821
UseSuspenseProductsResult,
1922
} from "./types"
@@ -168,6 +171,92 @@ export function createProductHooks<
168171
}
169172
}
170173

174+
function useInfiniteProducts(
175+
input: TListInput & ProductInfiniteInputBase
176+
): UseInfiniteProductsResult<TProduct> {
177+
const region = resolveRegion ? resolveRegion() : null
178+
const baseInput = { ...input } as TListInput & { enabled?: boolean }
179+
delete baseInput.enabled
180+
const resolvedInput = applyRegion(baseInput, region ?? undefined)
181+
const enabled =
182+
input.enabled ?? (!requireRegion || Boolean(resolvedInput.region_id))
183+
184+
const limitFromInput = (resolvedInput as { limit?: number }).limit
185+
const resolvedLimit =
186+
typeof limitFromInput === "number" && limitFromInput > 0
187+
? limitFromInput
188+
: defaultPageSize
189+
const offsetFromInput = (resolvedInput as { offset?: number }).offset
190+
const pageFromInput = resolvedInput.page ?? 1
191+
const baseOffset =
192+
typeof offsetFromInput === "number"
193+
? offsetFromInput
194+
: (pageFromInput - 1) * resolvedLimit
195+
196+
const baseListParams = buildList(resolvedInput)
197+
const baseQueryKey = resolvedQueryKeys.infinite
198+
? resolvedQueryKeys.infinite(baseListParams)
199+
: resolvedQueryKeys.list(baseListParams)
200+
const queryKey =
201+
resolvedQueryKeys.infinite ||
202+
baseQueryKey[baseQueryKey.length - 1] === "__infinite"
203+
? baseQueryKey
204+
: [...baseQueryKey, "__infinite"]
205+
206+
const {
207+
data,
208+
isLoading,
209+
isFetching,
210+
isFetchingNextPage,
211+
hasNextPage,
212+
fetchNextPage,
213+
refetch,
214+
error,
215+
} = useInfiniteQuery({
216+
queryKey,
217+
queryFn: ({ pageParam = baseOffset, signal }) => {
218+
const offset =
219+
typeof pageParam === "number" ? pageParam : baseOffset
220+
const page =
221+
resolvedLimit > 0 ? Math.floor(offset / resolvedLimit) + 1 : 1
222+
const pageInput = {
223+
...resolvedInput,
224+
page,
225+
limit: resolvedLimit,
226+
offset,
227+
} as TListInput & { offset?: number }
228+
const listParams = buildList(pageInput)
229+
return service.getProducts(listParams, signal)
230+
},
231+
initialPageParam: baseOffset,
232+
getNextPageParam: (lastPage) => {
233+
const limit = lastPage.limit ?? resolvedLimit
234+
const offset = lastPage.offset ?? 0
235+
const moreItemsExist = lastPage.count > offset + limit
236+
return moreItemsExist ? offset + limit : undefined
237+
},
238+
enabled,
239+
...resolvedCacheConfig.semiStatic,
240+
})
241+
242+
return {
243+
products: data?.pages.flatMap((page) => page.products) ?? [],
244+
isLoading,
245+
isFetching,
246+
isFetchingNextPage,
247+
hasNextPage: Boolean(hasNextPage),
248+
error:
249+
error instanceof Error ? error.message : error ? String(error) : null,
250+
totalCount: data?.pages[0]?.count ?? 0,
251+
fetchNextPage: () => {
252+
void fetchNextPage()
253+
},
254+
refetch: () => {
255+
void refetch()
256+
},
257+
}
258+
}
259+
171260
function useSuspenseProducts(
172261
input: TListInput
173262
): UseSuspenseProductsResult<TProduct> {
@@ -590,6 +679,7 @@ export function createProductHooks<
590679

591680
return {
592681
useProducts,
682+
useInfiniteProducts,
593683
useSuspenseProducts,
594684
useProduct,
595685
useSuspenseProduct,

libs/storefront-data/src/products/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ export { resolvePagination } from "./pagination"
33
export { createProductHooks } from "./hooks"
44
export type {
55
ProductDetailInputBase,
6+
ProductInfiniteInputBase,
67
ProductListInputBase,
78
ProductListResponse,
89
ProductQueryKeys,
910
ProductService,
11+
UseInfiniteProductsResult,
1012
UseProductsResult,
1113
UseSuspenseProductsResult,
1214
} from "./types"

libs/storefront-data/src/products/query-keys.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export function createProductQueryKeys<
1111
> {
1212
return {
1313
list: (params) => createQueryKey(namespace, "products", "list", params),
14+
infinite: (params) =>
15+
createQueryKey(namespace, "products", "infinite", params),
1416
detail: (params) => createQueryKey(namespace, "products", "detail", params),
1517
}
1618
}

libs/storefront-data/src/products/types.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ export type ProductListInputBase = RegionInfo & {
88
enabled?: boolean
99
}
1010

11+
export type ProductInfiniteInputBase = ProductListInputBase & {
12+
offset?: number
13+
}
14+
1115
export type ProductDetailInputBase = RegionInfo & {
1216
handle: string
1317
fields?: string
@@ -34,6 +38,7 @@ export type ProductService<TProduct, TListParams, TDetailParams> = {
3438

3539
export type ProductQueryKeys<TListParams, TDetailParams> = {
3640
list: (params: TListParams) => QueryKey
41+
infinite?: (params: TListParams) => QueryKey
3742
detail: (params: TDetailParams) => QueryKey
3843
}
3944

@@ -59,3 +64,15 @@ export type UseSuspenseProductsResult<TProduct> = {
5964
hasNextPage: boolean
6065
hasPrevPage: boolean
6166
}
67+
68+
export type UseInfiniteProductsResult<TProduct> = {
69+
products: TProduct[]
70+
isLoading: boolean
71+
isFetching: boolean
72+
isFetchingNextPage: boolean
73+
hasNextPage: boolean
74+
error: string | null
75+
totalCount: number
76+
fetchNextPage: () => void
77+
refetch: () => void
78+
}

0 commit comments

Comments
 (0)