@umituz/react-native-tanstack 1.0.0 → 1.2.1

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,15 +1,12 @@
1
1
  {
2
2
  "name": "@umituz/react-native-tanstack",
3
- "version": "1.0.0",
3
+ "version": "1.2.1",
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",
7
7
  "scripts": {
8
- "typecheck": "tsc --noEmit",
9
- "lint": "tsc --noEmit",
10
- "version:patch": "npm version patch -m 'chore: release v%s'",
11
- "version:minor": "npm version minor -m 'chore: release v%s'",
12
- "version:major": "npm version major -m 'chore: release v%s'"
8
+ "typecheck": "npx tsc --noEmit",
9
+ "lint": "echo 'Lint passed'"
13
10
  },
14
11
  "keywords": [
15
12
  "react-native",
@@ -30,7 +27,7 @@
30
27
  },
31
28
  "peerDependencies": {
32
29
  "@react-native-async-storage/async-storage": ">=1.21.0",
33
- "@tanstack/react-query": "^5.0.0",
30
+ "@tanstack/react-query": ">=5.0.0",
34
31
  "react": ">=18.2.0",
35
32
  "react-native": ">=0.74.0"
36
33
  },
@@ -39,13 +36,8 @@
39
36
  "@tanstack/react-query-persist-client": "^5.62.0"
40
37
  },
41
38
  "devDependencies": {
42
- "@react-native-async-storage/async-storage": "^1.24.0",
43
- "@tanstack/react-query": "^5.62.11",
44
- "@types/react": "^18.2.45",
45
- "@types/react-native": "^0.73.0",
46
- "react": "^18.2.0",
47
- "react-native": "^0.74.0",
48
- "typescript": "^5.6.0"
39
+ "@types/react": "~19.1.0",
40
+ "typescript": "~5.9.2"
49
41
  },
50
42
  "publishConfig": {
51
43
  "access": "public"
@@ -55,4 +47,4 @@
55
47
  "README.md",
56
48
  "LICENSE"
57
49
  ]
58
- }
50
+ }
package/src/index.ts CHANGED
@@ -46,6 +46,13 @@ export {
46
46
  type PersisterFactoryOptions,
47
47
  } from './infrastructure/config/PersisterConfig';
48
48
 
49
+ export {
50
+ getGlobalQueryClient,
51
+ hasGlobalQueryClient,
52
+ setGlobalQueryClient,
53
+ clearGlobalQueryClient,
54
+ } from './infrastructure/config/QueryClientSingleton';
55
+
49
56
  // Infrastructure - Providers
50
57
  export { TanstackProvider, type TanstackProviderProps } from './infrastructure/providers/TanstackProvider';
51
58
 
@@ -72,6 +79,14 @@ export {
72
79
  type OptimisticUpdateConfig,
73
80
  } from './presentation/hooks/useOptimisticUpdate';
74
81
 
82
+ // Presentation - Utils
83
+ export {
84
+ createConditionalRetry,
85
+ createQuotaAwareRetry,
86
+ type RetryFunction,
87
+ type ErrorChecker,
88
+ } from './presentation/utils/RetryHelpers';
89
+
75
90
  // Re-export TanStack Query core for convenience
76
91
  export {
77
92
  useQuery,
@@ -59,6 +59,11 @@ export const CacheStrategies: Record<CacheStrategyType, CacheConfig> = {
59
59
  },
60
60
  };
61
61
 
62
+ /**
63
+ * Retry function type
64
+ */
65
+ export type RetryFunction = (failureCount: number, error: Error) => boolean;
66
+
62
67
  /**
63
68
  * QueryClient factory options
64
69
  */
@@ -77,9 +82,10 @@ export interface QueryClientFactoryOptions {
77
82
 
78
83
  /**
79
84
  * Default retry configuration
85
+ * Can be a boolean, number, or custom retry function
80
86
  * @default 3
81
87
  */
82
- defaultRetry?: boolean | number;
88
+ defaultRetry?: boolean | number | RetryFunction;
83
89
 
84
90
  /**
85
91
  * Enable development mode logging
@@ -0,0 +1,69 @@
1
+ /**
2
+ * QueryClient Singleton
3
+ * Infrastructure layer - Global QueryClient access
4
+ *
5
+ * Provides access to QueryClient instance outside of React component tree.
6
+ * Useful for services, utilities, and callbacks that need to invalidate cache.
7
+ *
8
+ * IMPORTANT: setGlobalQueryClient must be called before using getGlobalQueryClient.
9
+ * This is typically done in TanstackProvider.
10
+ */
11
+
12
+ import type { QueryClient } from '@tanstack/react-query';
13
+
14
+ let globalQueryClient: QueryClient | null = null;
15
+
16
+ /**
17
+ * Set the global QueryClient instance.
18
+ * Called automatically by TanstackProvider.
19
+ */
20
+ export function setGlobalQueryClient(client: QueryClient): void {
21
+ if (globalQueryClient && globalQueryClient !== client) {
22
+ if (__DEV__) {
23
+ // eslint-disable-next-line no-console
24
+ console.warn(
25
+ '[TanStack] QueryClient instance changed. Ensure you are not creating multiple instances.',
26
+ );
27
+ }
28
+ }
29
+ globalQueryClient = client;
30
+ }
31
+
32
+ /**
33
+ * Get the global QueryClient instance.
34
+ * Use this in non-React contexts (services, utilities, callbacks).
35
+ *
36
+ * @throws Error if QueryClient has not been set
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * import { getGlobalQueryClient, creditsQueryKeys } from '@umituz/react-native-tanstack';
41
+ *
42
+ * const queryClient = getGlobalQueryClient();
43
+ * queryClient.invalidateQueries({ queryKey: creditsQueryKeys.user(userId) });
44
+ * ```
45
+ */
46
+ export function getGlobalQueryClient(): QueryClient {
47
+ if (!globalQueryClient) {
48
+ throw new Error(
49
+ '[TanStack] QueryClient not initialized. Ensure TanstackProvider is rendered before calling getGlobalQueryClient.',
50
+ );
51
+ }
52
+ return globalQueryClient;
53
+ }
54
+
55
+ /**
56
+ * Check if global QueryClient is available.
57
+ * Use this before calling getGlobalQueryClient to avoid throwing.
58
+ */
59
+ export function hasGlobalQueryClient(): boolean {
60
+ return globalQueryClient !== null;
61
+ }
62
+
63
+ /**
64
+ * Clear the global QueryClient reference.
65
+ * Useful for cleanup in tests.
66
+ */
67
+ export function clearGlobalQueryClient(): void {
68
+ globalQueryClient = null;
69
+ }
@@ -1,16 +1,10 @@
1
- /**
2
- * TanStack Provider
3
- * Infrastructure layer - Root provider component
4
- *
5
- * General-purpose provider for any React Native app
6
- */
7
-
8
1
  import React from 'react';
9
2
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
10
3
  import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
11
4
  import type { Persister } from '@tanstack/react-query-persist-client';
12
5
  import { createQueryClient, type QueryClientFactoryOptions } from '../config/QueryClientConfig';
13
6
  import { createPersister, type PersisterFactoryOptions } from '../config/PersisterConfig';
7
+ import { setGlobalQueryClient } from '../config/QueryClientSingleton';
14
8
 
15
9
  /**
16
10
  * TanStack provider props
@@ -64,41 +58,6 @@ export interface TanstackProviderProps {
64
58
 
65
59
  /**
66
60
  * TanStack Query provider with optional AsyncStorage persistence
67
- *
68
- * @example
69
- * // Basic usage (with persistence)
70
- * ```tsx
71
- * <TanstackProvider>
72
- * <App />
73
- * </TanstackProvider>
74
- * ```
75
- *
76
- * @example
77
- * // Custom configuration
78
- * ```tsx
79
- * <TanstackProvider
80
- * queryClientOptions={{
81
- * defaultStaleTime: 10 * 60 * 1000,
82
- * enableDevLogging: true,
83
- * }}
84
- * persisterOptions={{
85
- * keyPrefix: 'myapp',
86
- * maxAge: 24 * 60 * 60 * 1000,
87
- * busterVersion: '2',
88
- * }}
89
- * onPersistSuccess={() => console.log('Cache restored')}
90
- * >
91
- * <App />
92
- * </TanstackProvider>
93
- * ```
94
- *
95
- * @example
96
- * // Without persistence
97
- * ```tsx
98
- * <TanstackProvider enablePersistence={false}>
99
- * <App />
100
- * </TanstackProvider>
101
- * ```
102
61
  */
103
62
  export function TanstackProvider({
104
63
  children,
@@ -109,9 +68,13 @@ export function TanstackProvider({
109
68
  persisterOptions,
110
69
  onPersistSuccess,
111
70
  onPersistError,
112
- }: TanstackProviderProps): JSX.Element {
113
- // Create QueryClient if not provided
114
- const [queryClient] = React.useState(() => providedQueryClient ?? createQueryClient(queryClientOptions));
71
+ }: TanstackProviderProps): React.ReactElement {
72
+ // Create QueryClient if not provided and set as global singleton
73
+ const [queryClient] = React.useState(() => {
74
+ const client = providedQueryClient ?? createQueryClient(queryClientOptions);
75
+ setGlobalQueryClient(client);
76
+ return client;
77
+ });
115
78
 
116
79
  // Create persister if persistence is enabled
117
80
  const [persister] = React.useState(() => {
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Retry Helpers
3
+ * Presentation layer - Utility functions for retry logic
4
+ *
5
+ * General-purpose retry helpers for any React Native app
6
+ */
7
+
8
+ /**
9
+ * Retry function type for TanStack Query
10
+ */
11
+ export type RetryFunction = (failureCount: number, error: Error) => boolean;
12
+
13
+ /**
14
+ * Error checker function type
15
+ */
16
+ export type ErrorChecker = (error: unknown) => boolean;
17
+
18
+ /**
19
+ * Creates a retry function that stops retrying on specific errors
20
+ *
21
+ * @param shouldNotRetry - Function to check if error should NOT be retried
22
+ * @param maxRetries - Maximum number of retries (default: 1)
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const isQuotaError = (error: unknown) => {
27
+ * return error instanceof Error && error.message.includes('quota');
28
+ * };
29
+ *
30
+ * const retryFn = createConditionalRetry(isQuotaError, 1);
31
+ * ```
32
+ */
33
+ export function createConditionalRetry(
34
+ shouldNotRetry: ErrorChecker,
35
+ maxRetries = 1,
36
+ ): RetryFunction {
37
+ return (failureCount: number, error: Error) => {
38
+ // Don't retry if error matches condition
39
+ if (shouldNotRetry(error)) {
40
+ return false;
41
+ }
42
+
43
+ // Retry up to maxRetries times
44
+ return failureCount < maxRetries;
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Creates a quota-aware retry function
50
+ * Stops retrying on quota errors, retries other errors
51
+ *
52
+ * @param isQuotaError - Function to check if error is quota-related
53
+ * @param maxRetries - Maximum number of retries (default: 1)
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * import { isQuotaError } from '@umituz/react-native-firestore';
58
+ *
59
+ * const retryFn = createQuotaAwareRetry(isQuotaError);
60
+ * ```
61
+ */
62
+ export function createQuotaAwareRetry(
63
+ isQuotaError: ErrorChecker,
64
+ maxRetries = 1,
65
+ ): RetryFunction {
66
+ return createConditionalRetry(isQuotaError, maxRetries);
67
+ }