@umituz/react-native-tanstack 1.2.8 → 1.2.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-tanstack",
3
- "version": "1.2.8",
3
+ "version": "1.2.10",
4
4
  "description": "TanStack Query configuration and utilities for React Native apps - Pre-configured QueryClient with AsyncStorage persistence",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -1,68 +1,35 @@
1
1
  /**
2
2
  * useOptimisticUpdate Hook
3
3
  * Presentation layer - Optimistic update helper
4
- *
5
- * General-purpose optimistic updates for any React Native app
6
4
  */
7
5
 
8
- import { useMutation, useQueryClient, type UseMutationOptions } from '@tanstack/react-query';
6
+ import { useMutation, useQueryClient, type MutationOptions } from '@tanstack/react-query';
9
7
 
10
8
  /**
11
9
  * Optimistic update configuration
12
10
  */
13
11
  export interface OptimisticUpdateConfig<TData, TVariables> {
14
- /**
15
- * Query key to update optimistically
16
- */
17
12
  queryKey: readonly unknown[];
18
-
19
- /**
20
- * Function to update the cached data optimistically
21
- */
22
13
  updater: (oldData: TData | undefined, variables: TVariables) => TData;
23
-
24
- /**
25
- * Whether to invalidate the query after successful mutation
26
- * @default true
27
- */
28
14
  invalidateOnSuccess?: boolean;
29
15
  }
30
16
 
31
17
  /**
32
18
  * Hook for mutations with optimistic updates and automatic rollback
33
- *
34
- * @example
35
- * ```typescript
36
- * const updatePost = useOptimisticUpdate<Post, UpdatePostVariables>({
37
- * mutationFn: (variables) => api.updatePost(variables.id, variables.data),
38
- * queryKey: ['posts', postId],
39
- * updater: (oldPost, variables) => ({
40
- * ...oldPost,
41
- * ...variables.data,
42
- * }),
43
- * });
44
- *
45
- * // Usage
46
- * updatePost.mutate({ id: 123, data: { title: 'New Title' } });
47
- * ```
48
19
  */
49
20
  export function useOptimisticUpdate<TData = unknown, TVariables = unknown, TError = Error>(
50
21
  config: OptimisticUpdateConfig<TData, TVariables> &
51
- UseMutationOptions<TData, TError, TVariables>,
22
+ MutationOptions<TData, TError, TVariables>,
52
23
  ) {
53
24
  const queryClient = useQueryClient();
54
25
  const { queryKey, updater, invalidateOnSuccess = true, onError, onSettled, ...mutationOptions } = config;
55
26
 
56
27
  return useMutation({
57
28
  ...mutationOptions,
58
- onMutate: async (variables) => {
59
- // Cancel outgoing refetches to avoid overwriting optimistic update
29
+ onMutate: async (variables: TVariables) => {
60
30
  await queryClient.cancelQueries({ queryKey });
61
-
62
- // Snapshot the previous value
63
31
  const previousData = queryClient.getQueryData<TData>(queryKey);
64
32
 
65
- // Optimistically update to the new value
66
33
  if (previousData !== undefined) {
67
34
  const optimisticData = updater(previousData, variables);
68
35
  queryClient.setQueryData(queryKey, optimisticData);
@@ -73,11 +40,10 @@ export function useOptimisticUpdate<TData = unknown, TVariables = unknown, TErro
73
40
  }
74
41
  }
75
42
 
76
- // Return context with previous data for rollback
77
43
  return { previousData };
78
44
  },
79
- onError: (error, variables, context, ...rest) => {
80
- // Rollback to previous data on error
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
+ onError: (error: TError, variables: TVariables, context: any) => {
81
47
  if (context?.previousData !== undefined) {
82
48
  queryClient.setQueryData(queryKey, context.previousData);
83
49
 
@@ -87,40 +53,29 @@ export function useOptimisticUpdate<TData = unknown, TVariables = unknown, TErro
87
53
  }
88
54
  }
89
55
 
90
- // Call user-provided onError
91
56
  if (onError) {
92
- onError(error, variables, context, ...rest);
57
+ onError(error, variables, context);
93
58
  }
94
59
  },
95
- onSettled: (data, error, variables, context, ...rest) => {
96
- // Invalidate query to refetch with real data
60
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
+ onSettled: (data: TData | undefined, error: TError | null, variables: TVariables, context: any) => {
97
62
  if (invalidateOnSuccess && !error) {
98
63
  queryClient.invalidateQueries({ queryKey });
99
64
  }
100
65
 
101
- // Call user-provided onSettled
102
66
  if (onSettled) {
103
- onSettled(data, error, variables, context, ...rest);
67
+ onSettled(data, error, variables, context);
104
68
  }
105
69
  },
106
70
  });
107
71
  }
108
72
 
109
73
  /**
110
- * Hook for list mutations with optimistic updates (add/remove/update items)
111
- *
112
- * @example
113
- * ```typescript
114
- * const addPost = useOptimisticListUpdate<Post[], { title: string }>({
115
- * mutationFn: (variables) => api.createPost(variables),
116
- * queryKey: ['posts'],
117
- * updater: (oldPosts, newPost) => [...(oldPosts ?? []), newPost],
118
- * });
119
- * ```
74
+ * Hook for list mutations with optimistic updates
120
75
  */
121
76
  export function useOptimisticListUpdate<TData extends unknown[], TVariables = unknown>(
122
77
  config: OptimisticUpdateConfig<TData, TVariables> &
123
- UseMutationOptions<TData, Error, TVariables>,
78
+ MutationOptions<TData, Error, TVariables>,
124
79
  ) {
125
80
  return useOptimisticUpdate<TData, TVariables>(config);
126
81
  }
@@ -1,15 +1,9 @@
1
1
  /**
2
2
  * usePaginatedQuery Hook
3
3
  * Presentation layer - Pagination helper
4
- *
5
- * General-purpose pagination for any React Native app
6
4
  */
7
5
 
8
- import {
9
- useInfiniteQuery,
10
- type UseInfiniteQueryOptions,
11
- type InfiniteData,
12
- } from '@tanstack/react-query';
6
+ import { useInfiniteQuery } from '@tanstack/react-query';
13
7
  import { useMemo } from 'react';
14
8
 
15
9
  /**
@@ -47,49 +41,36 @@ export interface OffsetPaginatedResponse<TData> {
47
41
  limit: number;
48
42
  }
49
43
 
44
+ /**
45
+ * Cursor pagination options
46
+ */
47
+ export interface CursorPaginationOptions<TData> {
48
+ queryKey: readonly unknown[];
49
+ queryFn: (context: { pageParam?: string }) => Promise<CursorPaginatedResponse<TData>>;
50
+ limit?: number;
51
+ enabled?: boolean;
52
+ staleTime?: number;
53
+ gcTime?: number;
54
+ }
55
+
50
56
  /**
51
57
  * 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
58
  */
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;
59
+ export function useCursorPagination<TData>(options: CursorPaginationOptions<TData>) {
60
+ const { queryKey, queryFn, limit: _limit = 20, ...restOptions } = options;
80
61
 
81
62
  const result = useInfiniteQuery({
82
- ...queryOptions,
83
- initialPageParam: undefined,
63
+ queryKey,
64
+ queryFn: ({ pageParam }) => queryFn({ pageParam }),
65
+ initialPageParam: undefined as string | undefined,
84
66
  getNextPageParam: (lastPage: CursorPaginatedResponse<TData>) =>
85
67
  lastPage.hasMore ? lastPage.nextCursor : undefined,
68
+ ...restOptions,
86
69
  });
87
70
 
88
- // Flatten pages into single array for easier consumption
89
71
  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);
72
+ if (!result.data?.pages) return [];
73
+ return result.data.pages.flatMap((page) => page.items);
93
74
  }, [result.data]);
94
75
 
95
76
  return {
@@ -99,53 +80,41 @@ export function useCursorPagination<TData>(
99
80
  };
100
81
  }
101
82
 
83
+ /**
84
+ * Offset pagination options
85
+ */
86
+ export interface OffsetPaginationOptions<TData> {
87
+ queryKey: readonly unknown[];
88
+ queryFn: (context: { pageParam: OffsetPageParam }) => Promise<OffsetPaginatedResponse<TData>>;
89
+ limit?: number;
90
+ enabled?: boolean;
91
+ staleTime?: number;
92
+ gcTime?: number;
93
+ }
94
+
102
95
  /**
103
96
  * 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
97
  */
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;
98
+ export function useOffsetPagination<TData>(options: OffsetPaginationOptions<TData>) {
99
+ const { queryKey, queryFn, limit = 20, ...restOptions } = options;
129
100
 
130
101
  const result = useInfiniteQuery({
131
- ...queryOptions,
102
+ queryKey,
103
+ queryFn: ({ pageParam }) => queryFn({ pageParam }),
132
104
  initialPageParam: { offset: 0, limit },
133
105
  getNextPageParam: (lastPage: OffsetPaginatedResponse<TData>) => {
134
106
  const nextOffset = lastPage.offset + lastPage.limit;
135
107
  return nextOffset < lastPage.total ? { offset: nextOffset, limit } : undefined;
136
108
  },
109
+ ...restOptions,
137
110
  });
138
111
 
139
- // Flatten pages into single array
140
112
  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);
113
+ if (!result.data?.pages) return [];
114
+ return result.data.pages.flatMap((page) => page.items);
144
115
  }, [result.data]);
145
116
 
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;
117
+ const total = result.data?.pages?.[result.data.pages.length - 1]?.total ?? 0;
149
118
 
150
119
  return {
151
120
  ...result,