@umituz/react-native-tanstack 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,156 @@
1
+ /**
2
+ * usePaginatedQuery Hook
3
+ * Presentation layer - Pagination helper
4
+ *
5
+ * General-purpose pagination for any React Native app
6
+ */
7
+
8
+ import {
9
+ useInfiniteQuery,
10
+ type UseInfiniteQueryOptions,
11
+ type InfiniteData,
12
+ } from '@tanstack/react-query';
13
+ import { useMemo } from 'react';
14
+
15
+ /**
16
+ * Page parameter for cursor-based pagination
17
+ */
18
+ export interface CursorPageParam {
19
+ cursor?: string;
20
+ limit?: number;
21
+ }
22
+
23
+ /**
24
+ * Page parameter for offset-based pagination
25
+ */
26
+ export interface OffsetPageParam {
27
+ offset: number;
28
+ limit: number;
29
+ }
30
+
31
+ /**
32
+ * Paginated response with cursor
33
+ */
34
+ export interface CursorPaginatedResponse<TData> {
35
+ items: TData[];
36
+ nextCursor?: string;
37
+ hasMore: boolean;
38
+ }
39
+
40
+ /**
41
+ * Paginated response with offset
42
+ */
43
+ export interface OffsetPaginatedResponse<TData> {
44
+ items: TData[];
45
+ total: number;
46
+ offset: number;
47
+ limit: number;
48
+ }
49
+
50
+ /**
51
+ * Hook for cursor-based infinite scroll
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useCursorPagination({
56
+ * queryKey: ['posts'],
57
+ * queryFn: ({ pageParam }) => fetchPosts({ cursor: pageParam, limit: 20 }),
58
+ * limit: 20,
59
+ * });
60
+ *
61
+ * // Flatten all pages into single array
62
+ * const allPosts = data.pages.flatMap(page => page.items);
63
+ * ```
64
+ */
65
+ export function useCursorPagination<TData>(
66
+ options: Omit<
67
+ UseInfiniteQueryOptions<
68
+ CursorPaginatedResponse<TData>,
69
+ Error,
70
+ CursorPaginatedResponse<TData>,
71
+ readonly unknown[],
72
+ string | undefined
73
+ >,
74
+ 'getNextPageParam' | 'initialPageParam'
75
+ > & {
76
+ limit?: number;
77
+ },
78
+ ) {
79
+ const { limit = 20, ...queryOptions } = options;
80
+
81
+ const result = useInfiniteQuery({
82
+ ...queryOptions,
83
+ initialPageParam: undefined,
84
+ getNextPageParam: (lastPage: CursorPaginatedResponse<TData>) =>
85
+ lastPage.hasMore ? lastPage.nextCursor : undefined,
86
+ });
87
+
88
+ // Flatten pages into single array for easier consumption
89
+ const flatData = useMemo(() => {
90
+ const infiniteData = result.data as InfiniteData<CursorPaginatedResponse<TData>> | undefined;
91
+ if (!infiniteData?.pages) return [];
92
+ return infiniteData.pages.flatMap((page) => page.items);
93
+ }, [result.data]);
94
+
95
+ return {
96
+ ...result,
97
+ flatData,
98
+ totalItems: flatData.length,
99
+ };
100
+ }
101
+
102
+ /**
103
+ * Hook for offset-based pagination
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * const { data, fetchNextPage, hasNextPage } = useOffsetPagination({
108
+ * queryKey: ['posts'],
109
+ * queryFn: ({ pageParam }) => fetchPosts({ offset: pageParam.offset, limit: pageParam.limit }),
110
+ * limit: 20,
111
+ * });
112
+ * ```
113
+ */
114
+ export function useOffsetPagination<TData>(
115
+ options: Omit<
116
+ UseInfiniteQueryOptions<
117
+ OffsetPaginatedResponse<TData>,
118
+ Error,
119
+ OffsetPaginatedResponse<TData>,
120
+ readonly unknown[],
121
+ OffsetPageParam
122
+ >,
123
+ 'getNextPageParam' | 'initialPageParam'
124
+ > & {
125
+ limit?: number;
126
+ },
127
+ ) {
128
+ const { limit = 20, ...queryOptions } = options;
129
+
130
+ const result = useInfiniteQuery({
131
+ ...queryOptions,
132
+ initialPageParam: { offset: 0, limit },
133
+ getNextPageParam: (lastPage: OffsetPaginatedResponse<TData>) => {
134
+ const nextOffset = lastPage.offset + lastPage.limit;
135
+ return nextOffset < lastPage.total ? { offset: nextOffset, limit } : undefined;
136
+ },
137
+ });
138
+
139
+ // Flatten pages into single array
140
+ const flatData = useMemo(() => {
141
+ const infiniteData = result.data as InfiniteData<OffsetPaginatedResponse<TData>> | undefined;
142
+ if (!infiniteData?.pages) return [];
143
+ return infiniteData.pages.flatMap((page) => page.items);
144
+ }, [result.data]);
145
+
146
+ // Calculate total from last page
147
+ const infiniteData = result.data as InfiniteData<OffsetPaginatedResponse<TData>> | undefined;
148
+ const total = infiniteData?.pages?.[infiniteData.pages.length - 1]?.total ?? 0;
149
+
150
+ return {
151
+ ...result,
152
+ flatData,
153
+ totalItems: flatData.length,
154
+ total,
155
+ };
156
+ }