@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.
- package/README.md +206 -0
- package/package.json +58 -0
- package/src/domain/constants/CacheDefaults.ts +63 -0
- package/src/domain/types/CacheStrategy.ts +115 -0
- package/src/domain/utils/QueryKeyFactory.ts +134 -0
- package/src/index.ts +88 -0
- package/src/infrastructure/config/PersisterConfig.ts +162 -0
- package/src/infrastructure/config/QueryClientConfig.ts +148 -0
- package/src/infrastructure/providers/TanstackProvider.tsx +142 -0
- package/src/presentation/hooks/useInvalidateQueries.ts +128 -0
- package/src/presentation/hooks/useOptimisticUpdate.ts +126 -0
- package/src/presentation/hooks/usePaginatedQuery.ts +156 -0
|
@@ -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
|
+
}
|