@umituz/react-native-tanstack 1.2.7 → 1.2.9

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.7",
3
+ "version": "1.2.9",
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",
package/src/index.ts CHANGED
@@ -98,6 +98,7 @@ export {
98
98
  type UseQueryResult,
99
99
  type UseMutationResult,
100
100
  type UseInfiniteQueryResult,
101
+ type InfiniteData,
101
102
  type QueryKey,
102
103
  type QueryClient,
103
104
  } from '@tanstack/react-query';
@@ -1,68 +1,42 @@
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
6
  import { useMutation, useQueryClient, type UseMutationOptions } from '@tanstack/react-query';
9
7
 
8
+ /**
9
+ * Context type for optimistic updates
10
+ */
11
+ interface OptimisticContext<TData> {
12
+ previousData: TData | undefined;
13
+ }
14
+
10
15
  /**
11
16
  * Optimistic update configuration
12
17
  */
13
18
  export interface OptimisticUpdateConfig<TData, TVariables> {
14
- /**
15
- * Query key to update optimistically
16
- */
17
19
  queryKey: readonly unknown[];
18
-
19
- /**
20
- * Function to update the cached data optimistically
21
- */
22
20
  updater: (oldData: TData | undefined, variables: TVariables) => TData;
23
-
24
- /**
25
- * Whether to invalidate the query after successful mutation
26
- * @default true
27
- */
28
21
  invalidateOnSuccess?: boolean;
29
22
  }
30
23
 
31
24
  /**
32
25
  * 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
26
  */
49
27
  export function useOptimisticUpdate<TData = unknown, TVariables = unknown, TError = Error>(
50
28
  config: OptimisticUpdateConfig<TData, TVariables> &
51
- UseMutationOptions<TData, TError, TVariables>,
29
+ Omit<UseMutationOptions<TData, TError, TVariables, OptimisticContext<TData>>, 'onMutate'>,
52
30
  ) {
53
31
  const queryClient = useQueryClient();
54
32
  const { queryKey, updater, invalidateOnSuccess = true, onError, onSettled, ...mutationOptions } = config;
55
33
 
56
- return useMutation({
34
+ return useMutation<TData, TError, TVariables, OptimisticContext<TData>>({
57
35
  ...mutationOptions,
58
36
  onMutate: async (variables) => {
59
- // Cancel outgoing refetches to avoid overwriting optimistic update
60
37
  await queryClient.cancelQueries({ queryKey });
61
-
62
- // Snapshot the previous value
63
38
  const previousData = queryClient.getQueryData<TData>(queryKey);
64
39
 
65
- // Optimistically update to the new value
66
40
  if (previousData !== undefined) {
67
41
  const optimisticData = updater(previousData, variables);
68
42
  queryClient.setQueryData(queryKey, optimisticData);
@@ -73,11 +47,9 @@ export function useOptimisticUpdate<TData = unknown, TVariables = unknown, TErro
73
47
  }
74
48
  }
75
49
 
76
- // Return context with previous data for rollback
77
50
  return { previousData };
78
51
  },
79
- onError: (error, variables, context, ...rest) => {
80
- // Rollback to previous data on error
52
+ onError: (error, variables, context) => {
81
53
  if (context?.previousData !== undefined) {
82
54
  queryClient.setQueryData(queryKey, context.previousData);
83
55
 
@@ -87,40 +59,28 @@ export function useOptimisticUpdate<TData = unknown, TVariables = unknown, TErro
87
59
  }
88
60
  }
89
61
 
90
- // Call user-provided onError
91
62
  if (onError) {
92
- onError(error, variables, context, ...rest);
63
+ onError(error, variables, context);
93
64
  }
94
65
  },
95
- onSettled: (data, error, variables, context, ...rest) => {
96
- // Invalidate query to refetch with real data
66
+ onSettled: (data, error, variables, context) => {
97
67
  if (invalidateOnSuccess && !error) {
98
68
  queryClient.invalidateQueries({ queryKey });
99
69
  }
100
70
 
101
- // Call user-provided onSettled
102
71
  if (onSettled) {
103
- onSettled(data, error, variables, context, ...rest);
72
+ onSettled(data, error, variables, context);
104
73
  }
105
74
  },
106
75
  });
107
76
  }
108
77
 
109
78
  /**
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
- * ```
79
+ * Hook for list mutations with optimistic updates
120
80
  */
121
81
  export function useOptimisticListUpdate<TData extends unknown[], TVariables = unknown>(
122
82
  config: OptimisticUpdateConfig<TData, TVariables> &
123
- UseMutationOptions<TData, Error, TVariables>,
83
+ Omit<UseMutationOptions<TData, Error, TVariables, OptimisticContext<TData>>, 'onMutate'>,
124
84
  ) {
125
85
  return useOptimisticUpdate<TData, TVariables>(config);
126
86
  }
@@ -1,14 +1,11 @@
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
6
  import {
9
7
  useInfiniteQuery,
10
8
  type UseInfiniteQueryOptions,
11
- type InfiniteData,
12
9
  } from '@tanstack/react-query';
13
10
  import { useMemo } from 'react';
14
11
 
@@ -49,18 +46,6 @@ export interface OffsetPaginatedResponse<TData> {
49
46
 
50
47
  /**
51
48
  * 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
49
  */
65
50
  export function useCursorPagination<TData>(
66
51
  options: Omit<
@@ -68,6 +53,7 @@ export function useCursorPagination<TData>(
68
53
  CursorPaginatedResponse<TData>,
69
54
  Error,
70
55
  CursorPaginatedResponse<TData>,
56
+ CursorPaginatedResponse<TData>,
71
57
  readonly unknown[],
72
58
  string | undefined
73
59
  >,
@@ -76,7 +62,7 @@ export function useCursorPagination<TData>(
76
62
  limit?: number;
77
63
  },
78
64
  ) {
79
- const { limit = 20, ...queryOptions } = options;
65
+ const { limit: _limit = 20, ...queryOptions } = options;
80
66
 
81
67
  const result = useInfiniteQuery({
82
68
  ...queryOptions,
@@ -85,11 +71,9 @@ export function useCursorPagination<TData>(
85
71
  lastPage.hasMore ? lastPage.nextCursor : undefined,
86
72
  });
87
73
 
88
- // Flatten pages into single array for easier consumption
89
74
  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);
75
+ if (!result.data?.pages) return [];
76
+ return result.data.pages.flatMap((page) => page.items);
93
77
  }, [result.data]);
94
78
 
95
79
  return {
@@ -101,15 +85,6 @@ export function useCursorPagination<TData>(
101
85
 
102
86
  /**
103
87
  * 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
88
  */
114
89
  export function useOffsetPagination<TData>(
115
90
  options: Omit<
@@ -117,6 +92,7 @@ export function useOffsetPagination<TData>(
117
92
  OffsetPaginatedResponse<TData>,
118
93
  Error,
119
94
  OffsetPaginatedResponse<TData>,
95
+ OffsetPaginatedResponse<TData>,
120
96
  readonly unknown[],
121
97
  OffsetPageParam
122
98
  >,
@@ -136,16 +112,12 @@ export function useOffsetPagination<TData>(
136
112
  },
137
113
  });
138
114
 
139
- // Flatten pages into single array
140
115
  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);
116
+ if (!result.data?.pages) return [];
117
+ return result.data.pages.flatMap((page) => page.items);
144
118
  }, [result.data]);
145
119
 
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;
120
+ const total = result.data?.pages?.[result.data.pages.length - 1]?.total ?? 0;
149
121
 
150
122
  return {
151
123
  ...result,