@croacroa/react-native-template 1.0.0 → 2.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/.github/workflows/ci.yml +187 -184
- package/.github/workflows/eas-build.yml +55 -55
- package/.github/workflows/eas-update.yml +50 -50
- package/CHANGELOG.md +106 -106
- package/CONTRIBUTING.md +377 -377
- package/README.md +399 -399
- package/__tests__/components/snapshots.test.tsx +131 -0
- package/__tests__/integration/auth-api.test.tsx +227 -0
- package/__tests__/performance/VirtualizedList.perf.test.tsx +362 -0
- package/app/(public)/onboarding.tsx +5 -5
- package/app.config.ts +45 -2
- package/assets/images/.gitkeep +7 -7
- package/components/onboarding/OnboardingScreen.tsx +370 -370
- package/components/onboarding/index.ts +2 -2
- package/components/providers/SuspenseBoundary.tsx +357 -0
- package/components/providers/index.ts +13 -0
- package/components/ui/Avatar.tsx +316 -316
- package/components/ui/Badge.tsx +416 -416
- package/components/ui/BottomSheet.tsx +307 -307
- package/components/ui/Checkbox.tsx +261 -261
- package/components/ui/OptimizedImage.tsx +369 -369
- package/components/ui/Select.tsx +240 -240
- package/components/ui/VirtualizedList.tsx +285 -0
- package/components/ui/index.ts +23 -18
- package/constants/config.ts +97 -54
- package/docs/adr/001-state-management.md +79 -79
- package/docs/adr/002-styling-approach.md +130 -130
- package/docs/adr/003-data-fetching.md +155 -155
- package/docs/adr/004-auth-adapter-pattern.md +144 -144
- package/docs/adr/README.md +78 -78
- package/hooks/index.ts +27 -25
- package/hooks/useApi.ts +102 -5
- package/hooks/useAuth.tsx +82 -0
- package/hooks/useBiometrics.ts +295 -295
- package/hooks/useDeepLinking.ts +256 -256
- package/hooks/useMFA.ts +499 -0
- package/hooks/useNotifications.ts +39 -0
- package/hooks/useOffline.ts +32 -2
- package/hooks/usePerformance.ts +434 -434
- package/hooks/useTheme.tsx +76 -0
- package/hooks/useUpdates.ts +358 -358
- package/i18n/index.ts +194 -77
- package/i18n/locales/ar.json +101 -0
- package/i18n/locales/de.json +101 -0
- package/i18n/locales/en.json +101 -101
- package/i18n/locales/es.json +101 -0
- package/i18n/locales/fr.json +101 -101
- package/jest.config.js +4 -4
- package/maestro/README.md +113 -113
- package/maestro/config.yaml +35 -35
- package/maestro/flows/login.yaml +62 -62
- package/maestro/flows/mfa-login.yaml +92 -0
- package/maestro/flows/mfa-setup.yaml +86 -0
- package/maestro/flows/navigation.yaml +68 -68
- package/maestro/flows/offline-conflict.yaml +101 -0
- package/maestro/flows/offline-sync.yaml +128 -0
- package/maestro/flows/offline.yaml +60 -60
- package/maestro/flows/register.yaml +94 -94
- package/package.json +175 -170
- package/services/analytics.ts +428 -428
- package/services/api.ts +340 -340
- package/services/authAdapter.ts +333 -333
- package/services/backgroundSync.ts +626 -0
- package/services/index.ts +54 -22
- package/services/security.ts +229 -0
- package/tailwind.config.js +47 -47
- package/utils/accessibility.ts +446 -446
- package/utils/index.ts +52 -43
- package/utils/withAccessibility.tsx +272 -0
|
@@ -1,130 +1,130 @@
|
|
|
1
|
-
# ADR-002: Use NativeWind (Tailwind CSS) for Styling
|
|
2
|
-
|
|
3
|
-
## Status
|
|
4
|
-
|
|
5
|
-
Accepted
|
|
6
|
-
|
|
7
|
-
## Date
|
|
8
|
-
|
|
9
|
-
2024-01-01
|
|
10
|
-
|
|
11
|
-
## Context
|
|
12
|
-
|
|
13
|
-
We need a consistent styling approach for our React Native application. The requirements are:
|
|
14
|
-
|
|
15
|
-
1. Developer productivity (fast to write styles)
|
|
16
|
-
2. Consistent design system
|
|
17
|
-
3. Dark mode support
|
|
18
|
-
4. Good performance
|
|
19
|
-
5. Familiar to web developers
|
|
20
|
-
|
|
21
|
-
Options considered:
|
|
22
|
-
|
|
23
|
-
- **StyleSheet.create**: React Native's built-in solution
|
|
24
|
-
- **Styled Components**: CSS-in-JS, popular in web
|
|
25
|
-
- **NativeWind**: Tailwind CSS for React Native
|
|
26
|
-
- **Tamagui**: Universal design system
|
|
27
|
-
- **Dripsy**: Responsive design system
|
|
28
|
-
|
|
29
|
-
## Decision
|
|
30
|
-
|
|
31
|
-
We chose **NativeWind** (Tailwind CSS for React Native).
|
|
32
|
-
|
|
33
|
-
## Rationale
|
|
34
|
-
|
|
35
|
-
1. **Developer Experience**: Utility classes are fast to write
|
|
36
|
-
|
|
37
|
-
```tsx
|
|
38
|
-
// NativeWind
|
|
39
|
-
<View className="p-4 bg-white rounded-xl shadow-lg">
|
|
40
|
-
|
|
41
|
-
// StyleSheet
|
|
42
|
-
<View style={[styles.container, styles.rounded, styles.shadow]}>
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
2. **Design System**: Tailwind's design tokens ensure consistency
|
|
46
|
-
|
|
47
|
-
3. **Dark Mode**: Built-in support with `dark:` prefix
|
|
48
|
-
|
|
49
|
-
```tsx
|
|
50
|
-
<View className="bg-white dark:bg-slate-900">
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
4. **Responsive Design**: Support for breakpoints
|
|
54
|
-
|
|
55
|
-
```tsx
|
|
56
|
-
<View className="p-2 md:p-4 lg:p-6">
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
5. **Familiarity**: Web developers already know Tailwind
|
|
60
|
-
|
|
61
|
-
6. **Performance**: Compiled at build time, no runtime overhead
|
|
62
|
-
|
|
63
|
-
## Consequences
|
|
64
|
-
|
|
65
|
-
### Positive
|
|
66
|
-
|
|
67
|
-
- Faster styling with utility classes
|
|
68
|
-
- Consistent spacing, colors, typography
|
|
69
|
-
- Easy dark mode implementation
|
|
70
|
-
- Small runtime footprint
|
|
71
|
-
- Web developers can contribute quickly
|
|
72
|
-
|
|
73
|
-
### Negative
|
|
74
|
-
|
|
75
|
-
- Long className strings can be hard to read
|
|
76
|
-
- Learning curve for Tailwind utilities
|
|
77
|
-
- Some RN-specific styles need custom config
|
|
78
|
-
|
|
79
|
-
### Mitigation
|
|
80
|
-
|
|
81
|
-
- Use `cn()` utility to organize classes
|
|
82
|
-
- Create component abstractions for complex patterns
|
|
83
|
-
- Document custom Tailwind config
|
|
84
|
-
|
|
85
|
-
## Implementation
|
|
86
|
-
|
|
87
|
-
### Configuration
|
|
88
|
-
|
|
89
|
-
```js
|
|
90
|
-
// tailwind.config.js
|
|
91
|
-
module.exports = {
|
|
92
|
-
content: ["./app/**/*.{js,tsx}", "./components/**/*.{js,tsx}"],
|
|
93
|
-
presets: [require("nativewind/preset")],
|
|
94
|
-
theme: {
|
|
95
|
-
extend: {
|
|
96
|
-
colors: {
|
|
97
|
-
primary: {
|
|
98
|
-
/* ... */
|
|
99
|
-
},
|
|
100
|
-
background: { light: "#fff", dark: "#0f172a" },
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
};
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Usage Pattern
|
|
108
|
-
|
|
109
|
-
```tsx
|
|
110
|
-
import { cn } from "@/utils/cn";
|
|
111
|
-
|
|
112
|
-
function Card({ className, ...props }) {
|
|
113
|
-
return (
|
|
114
|
-
<View
|
|
115
|
-
className={cn(
|
|
116
|
-
"p-4 rounded-xl",
|
|
117
|
-
"bg-white dark:bg-slate-800",
|
|
118
|
-
"border border-gray-200 dark:border-gray-700",
|
|
119
|
-
className
|
|
120
|
-
)}
|
|
121
|
-
{...props}
|
|
122
|
-
/>
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## References
|
|
128
|
-
|
|
129
|
-
- [NativeWind Documentation](https://www.nativewind.dev/)
|
|
130
|
-
- [Tailwind CSS](https://tailwindcss.com/)
|
|
1
|
+
# ADR-002: Use NativeWind (Tailwind CSS) for Styling
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted
|
|
6
|
+
|
|
7
|
+
## Date
|
|
8
|
+
|
|
9
|
+
2024-01-01
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
We need a consistent styling approach for our React Native application. The requirements are:
|
|
14
|
+
|
|
15
|
+
1. Developer productivity (fast to write styles)
|
|
16
|
+
2. Consistent design system
|
|
17
|
+
3. Dark mode support
|
|
18
|
+
4. Good performance
|
|
19
|
+
5. Familiar to web developers
|
|
20
|
+
|
|
21
|
+
Options considered:
|
|
22
|
+
|
|
23
|
+
- **StyleSheet.create**: React Native's built-in solution
|
|
24
|
+
- **Styled Components**: CSS-in-JS, popular in web
|
|
25
|
+
- **NativeWind**: Tailwind CSS for React Native
|
|
26
|
+
- **Tamagui**: Universal design system
|
|
27
|
+
- **Dripsy**: Responsive design system
|
|
28
|
+
|
|
29
|
+
## Decision
|
|
30
|
+
|
|
31
|
+
We chose **NativeWind** (Tailwind CSS for React Native).
|
|
32
|
+
|
|
33
|
+
## Rationale
|
|
34
|
+
|
|
35
|
+
1. **Developer Experience**: Utility classes are fast to write
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
// NativeWind
|
|
39
|
+
<View className="p-4 bg-white rounded-xl shadow-lg">
|
|
40
|
+
|
|
41
|
+
// StyleSheet
|
|
42
|
+
<View style={[styles.container, styles.rounded, styles.shadow]}>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
2. **Design System**: Tailwind's design tokens ensure consistency
|
|
46
|
+
|
|
47
|
+
3. **Dark Mode**: Built-in support with `dark:` prefix
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<View className="bg-white dark:bg-slate-900">
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
4. **Responsive Design**: Support for breakpoints
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
<View className="p-2 md:p-4 lg:p-6">
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
5. **Familiarity**: Web developers already know Tailwind
|
|
60
|
+
|
|
61
|
+
6. **Performance**: Compiled at build time, no runtime overhead
|
|
62
|
+
|
|
63
|
+
## Consequences
|
|
64
|
+
|
|
65
|
+
### Positive
|
|
66
|
+
|
|
67
|
+
- Faster styling with utility classes
|
|
68
|
+
- Consistent spacing, colors, typography
|
|
69
|
+
- Easy dark mode implementation
|
|
70
|
+
- Small runtime footprint
|
|
71
|
+
- Web developers can contribute quickly
|
|
72
|
+
|
|
73
|
+
### Negative
|
|
74
|
+
|
|
75
|
+
- Long className strings can be hard to read
|
|
76
|
+
- Learning curve for Tailwind utilities
|
|
77
|
+
- Some RN-specific styles need custom config
|
|
78
|
+
|
|
79
|
+
### Mitigation
|
|
80
|
+
|
|
81
|
+
- Use `cn()` utility to organize classes
|
|
82
|
+
- Create component abstractions for complex patterns
|
|
83
|
+
- Document custom Tailwind config
|
|
84
|
+
|
|
85
|
+
## Implementation
|
|
86
|
+
|
|
87
|
+
### Configuration
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
// tailwind.config.js
|
|
91
|
+
module.exports = {
|
|
92
|
+
content: ["./app/**/*.{js,tsx}", "./components/**/*.{js,tsx}"],
|
|
93
|
+
presets: [require("nativewind/preset")],
|
|
94
|
+
theme: {
|
|
95
|
+
extend: {
|
|
96
|
+
colors: {
|
|
97
|
+
primary: {
|
|
98
|
+
/* ... */
|
|
99
|
+
},
|
|
100
|
+
background: { light: "#fff", dark: "#0f172a" },
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Usage Pattern
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { cn } from "@/utils/cn";
|
|
111
|
+
|
|
112
|
+
function Card({ className, ...props }) {
|
|
113
|
+
return (
|
|
114
|
+
<View
|
|
115
|
+
className={cn(
|
|
116
|
+
"p-4 rounded-xl",
|
|
117
|
+
"bg-white dark:bg-slate-800",
|
|
118
|
+
"border border-gray-200 dark:border-gray-700",
|
|
119
|
+
className
|
|
120
|
+
)}
|
|
121
|
+
{...props}
|
|
122
|
+
/>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## References
|
|
128
|
+
|
|
129
|
+
- [NativeWind Documentation](https://www.nativewind.dev/)
|
|
130
|
+
- [Tailwind CSS](https://tailwindcss.com/)
|
|
@@ -1,155 +1,155 @@
|
|
|
1
|
-
# ADR-003: Use React Query for Data Fetching
|
|
2
|
-
|
|
3
|
-
## Status
|
|
4
|
-
|
|
5
|
-
Accepted
|
|
6
|
-
|
|
7
|
-
## Date
|
|
8
|
-
|
|
9
|
-
2024-01-01
|
|
10
|
-
|
|
11
|
-
## Context
|
|
12
|
-
|
|
13
|
-
We need a data fetching solution that handles:
|
|
14
|
-
|
|
15
|
-
1. Caching and cache invalidation
|
|
16
|
-
2. Loading and error states
|
|
17
|
-
3. Optimistic updates
|
|
18
|
-
4. Offline support
|
|
19
|
-
5. Request deduplication
|
|
20
|
-
6. Background refetching
|
|
21
|
-
|
|
22
|
-
Options considered:
|
|
23
|
-
|
|
24
|
-
- **fetch/axios only**: Manual state management
|
|
25
|
-
- **SWR**: Lightweight, good for web
|
|
26
|
-
- **React Query (TanStack Query)**: Full-featured
|
|
27
|
-
- **RTK Query**: Redux-based
|
|
28
|
-
- **Apollo Client**: GraphQL-focused
|
|
29
|
-
|
|
30
|
-
## Decision
|
|
31
|
-
|
|
32
|
-
We chose **React Query (TanStack Query)** for server state management.
|
|
33
|
-
|
|
34
|
-
## Rationale
|
|
35
|
-
|
|
36
|
-
1. **Comprehensive Feature Set**
|
|
37
|
-
- Automatic caching and cache invalidation
|
|
38
|
-
- Background refetching
|
|
39
|
-
- Optimistic updates
|
|
40
|
-
- Infinite queries for pagination
|
|
41
|
-
- Offline support with persistence
|
|
42
|
-
|
|
43
|
-
2. **Excellent Developer Experience**
|
|
44
|
-
|
|
45
|
-
```tsx
|
|
46
|
-
const { data, isLoading, error } = useQuery({
|
|
47
|
-
queryKey: ["users", userId],
|
|
48
|
-
queryFn: () => api.get(`/users/${userId}`),
|
|
49
|
-
});
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
3. **Offline Support**: Works great with AsyncStorage persister
|
|
53
|
-
|
|
54
|
-
```tsx
|
|
55
|
-
const persister = createAsyncStoragePersister({
|
|
56
|
-
storage: AsyncStorage,
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
<PersistQueryClientProvider
|
|
60
|
-
client={queryClient}
|
|
61
|
-
persistOptions={{ persister }}
|
|
62
|
-
>
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
4. **Performance**: Smart refetching, request deduplication
|
|
66
|
-
|
|
67
|
-
5. **DevTools**: React Query DevTools for debugging
|
|
68
|
-
|
|
69
|
-
## Consequences
|
|
70
|
-
|
|
71
|
-
### Positive
|
|
72
|
-
|
|
73
|
-
- No need for manual cache management
|
|
74
|
-
- Automatic loading/error states
|
|
75
|
-
- Works offline with persisted cache
|
|
76
|
-
- Reduces boilerplate significantly
|
|
77
|
-
- Battle-tested and well-documented
|
|
78
|
-
|
|
79
|
-
### Negative
|
|
80
|
-
|
|
81
|
-
- Learning curve for query keys and cache invalidation
|
|
82
|
-
- Can be overkill for simple apps
|
|
83
|
-
- Adds bundle size (~12KB gzipped)
|
|
84
|
-
|
|
85
|
-
### Mitigation
|
|
86
|
-
|
|
87
|
-
- Create query key factories for consistency
|
|
88
|
-
- Document common patterns
|
|
89
|
-
- Create custom hooks for reusable queries
|
|
90
|
-
|
|
91
|
-
## Implementation
|
|
92
|
-
|
|
93
|
-
### Query Client Configuration
|
|
94
|
-
|
|
95
|
-
```ts
|
|
96
|
-
// services/queryClient.ts
|
|
97
|
-
export const queryClient = new QueryClient({
|
|
98
|
-
defaultOptions: {
|
|
99
|
-
queries: {
|
|
100
|
-
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
101
|
-
gcTime: 30 * 60 * 1000, // 30 minutes
|
|
102
|
-
retry: 3,
|
|
103
|
-
refetchOnReconnect: true,
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Query Keys Factory
|
|
110
|
-
|
|
111
|
-
```ts
|
|
112
|
-
// hooks/useApi.ts
|
|
113
|
-
export const queryKeys = {
|
|
114
|
-
users: {
|
|
115
|
-
all: ["users"] as const,
|
|
116
|
-
detail: (id: string) => ["users", id] as const,
|
|
117
|
-
me: () => ["users", "me"] as const,
|
|
118
|
-
},
|
|
119
|
-
posts: {
|
|
120
|
-
all: ["posts"] as const,
|
|
121
|
-
list: (filters: PostFilters) => ["posts", filters] as const,
|
|
122
|
-
detail: (id: string) => ["posts", id] as const,
|
|
123
|
-
},
|
|
124
|
-
};
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Custom Hook Pattern
|
|
128
|
-
|
|
129
|
-
```ts
|
|
130
|
-
export function useUser(userId: string) {
|
|
131
|
-
return useQuery({
|
|
132
|
-
queryKey: queryKeys.users.detail(userId),
|
|
133
|
-
queryFn: () => api.get<User>(`/users/${userId}`),
|
|
134
|
-
enabled: !!userId,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function useUpdateUser() {
|
|
139
|
-
const queryClient = useQueryClient();
|
|
140
|
-
|
|
141
|
-
return useMutation({
|
|
142
|
-
mutationFn: (data: UpdateUserInput) => api.patch<User>("/users/me", data),
|
|
143
|
-
onSuccess: () => {
|
|
144
|
-
queryClient.invalidateQueries({
|
|
145
|
-
queryKey: queryKeys.users.me(),
|
|
146
|
-
});
|
|
147
|
-
},
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## References
|
|
153
|
-
|
|
154
|
-
- [TanStack Query Documentation](https://tanstack.com/query)
|
|
155
|
-
- [React Query Offline Support](https://tanstack.com/query/latest/docs/react/plugins/persistQueryClient)
|
|
1
|
+
# ADR-003: Use React Query for Data Fetching
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted
|
|
6
|
+
|
|
7
|
+
## Date
|
|
8
|
+
|
|
9
|
+
2024-01-01
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
We need a data fetching solution that handles:
|
|
14
|
+
|
|
15
|
+
1. Caching and cache invalidation
|
|
16
|
+
2. Loading and error states
|
|
17
|
+
3. Optimistic updates
|
|
18
|
+
4. Offline support
|
|
19
|
+
5. Request deduplication
|
|
20
|
+
6. Background refetching
|
|
21
|
+
|
|
22
|
+
Options considered:
|
|
23
|
+
|
|
24
|
+
- **fetch/axios only**: Manual state management
|
|
25
|
+
- **SWR**: Lightweight, good for web
|
|
26
|
+
- **React Query (TanStack Query)**: Full-featured
|
|
27
|
+
- **RTK Query**: Redux-based
|
|
28
|
+
- **Apollo Client**: GraphQL-focused
|
|
29
|
+
|
|
30
|
+
## Decision
|
|
31
|
+
|
|
32
|
+
We chose **React Query (TanStack Query)** for server state management.
|
|
33
|
+
|
|
34
|
+
## Rationale
|
|
35
|
+
|
|
36
|
+
1. **Comprehensive Feature Set**
|
|
37
|
+
- Automatic caching and cache invalidation
|
|
38
|
+
- Background refetching
|
|
39
|
+
- Optimistic updates
|
|
40
|
+
- Infinite queries for pagination
|
|
41
|
+
- Offline support with persistence
|
|
42
|
+
|
|
43
|
+
2. **Excellent Developer Experience**
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
const { data, isLoading, error } = useQuery({
|
|
47
|
+
queryKey: ["users", userId],
|
|
48
|
+
queryFn: () => api.get(`/users/${userId}`),
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
3. **Offline Support**: Works great with AsyncStorage persister
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
const persister = createAsyncStoragePersister({
|
|
56
|
+
storage: AsyncStorage,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
<PersistQueryClientProvider
|
|
60
|
+
client={queryClient}
|
|
61
|
+
persistOptions={{ persister }}
|
|
62
|
+
>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
4. **Performance**: Smart refetching, request deduplication
|
|
66
|
+
|
|
67
|
+
5. **DevTools**: React Query DevTools for debugging
|
|
68
|
+
|
|
69
|
+
## Consequences
|
|
70
|
+
|
|
71
|
+
### Positive
|
|
72
|
+
|
|
73
|
+
- No need for manual cache management
|
|
74
|
+
- Automatic loading/error states
|
|
75
|
+
- Works offline with persisted cache
|
|
76
|
+
- Reduces boilerplate significantly
|
|
77
|
+
- Battle-tested and well-documented
|
|
78
|
+
|
|
79
|
+
### Negative
|
|
80
|
+
|
|
81
|
+
- Learning curve for query keys and cache invalidation
|
|
82
|
+
- Can be overkill for simple apps
|
|
83
|
+
- Adds bundle size (~12KB gzipped)
|
|
84
|
+
|
|
85
|
+
### Mitigation
|
|
86
|
+
|
|
87
|
+
- Create query key factories for consistency
|
|
88
|
+
- Document common patterns
|
|
89
|
+
- Create custom hooks for reusable queries
|
|
90
|
+
|
|
91
|
+
## Implementation
|
|
92
|
+
|
|
93
|
+
### Query Client Configuration
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
// services/queryClient.ts
|
|
97
|
+
export const queryClient = new QueryClient({
|
|
98
|
+
defaultOptions: {
|
|
99
|
+
queries: {
|
|
100
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
101
|
+
gcTime: 30 * 60 * 1000, // 30 minutes
|
|
102
|
+
retry: 3,
|
|
103
|
+
refetchOnReconnect: true,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Query Keys Factory
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
// hooks/useApi.ts
|
|
113
|
+
export const queryKeys = {
|
|
114
|
+
users: {
|
|
115
|
+
all: ["users"] as const,
|
|
116
|
+
detail: (id: string) => ["users", id] as const,
|
|
117
|
+
me: () => ["users", "me"] as const,
|
|
118
|
+
},
|
|
119
|
+
posts: {
|
|
120
|
+
all: ["posts"] as const,
|
|
121
|
+
list: (filters: PostFilters) => ["posts", filters] as const,
|
|
122
|
+
detail: (id: string) => ["posts", id] as const,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Custom Hook Pattern
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
export function useUser(userId: string) {
|
|
131
|
+
return useQuery({
|
|
132
|
+
queryKey: queryKeys.users.detail(userId),
|
|
133
|
+
queryFn: () => api.get<User>(`/users/${userId}`),
|
|
134
|
+
enabled: !!userId,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function useUpdateUser() {
|
|
139
|
+
const queryClient = useQueryClient();
|
|
140
|
+
|
|
141
|
+
return useMutation({
|
|
142
|
+
mutationFn: (data: UpdateUserInput) => api.patch<User>("/users/me", data),
|
|
143
|
+
onSuccess: () => {
|
|
144
|
+
queryClient.invalidateQueries({
|
|
145
|
+
queryKey: queryKeys.users.me(),
|
|
146
|
+
});
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## References
|
|
153
|
+
|
|
154
|
+
- [TanStack Query Documentation](https://tanstack.com/query)
|
|
155
|
+
- [React Query Offline Support](https://tanstack.com/query/latest/docs/react/plugins/persistQueryClient)
|