ginskill-init 2.7.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/.wrangler/cache/pages.json +4 -0
- package/.wrangler/cache/wrangler-account.json +6 -0
- package/DEVELOPMENT.md +510 -0
- package/README.md +104 -0
- package/agents/developer.md +56 -0
- package/agents/frontend-design.md +69 -0
- package/agents/mobile-reviewer.md +36 -0
- package/agents/review-code.md +49 -0
- package/agents/security-scanner.md +50 -0
- package/agents/tester.md +72 -0
- package/bin/cli.js +461 -0
- package/landing/ai-build-ai.png +0 -0
- package/landing/index.html +1495 -0
- package/landing/logo.png +0 -0
- package/package.json +37 -0
- package/skills/active-life-dev/SKILL.md +157 -0
- package/skills/active-life-dev/docs/auth.md +187 -0
- package/skills/active-life-dev/docs/customers.md +216 -0
- package/skills/active-life-dev/docs/integrations.md +209 -0
- package/skills/active-life-dev/docs/inventory.md +192 -0
- package/skills/active-life-dev/docs/modules.md +181 -0
- package/skills/active-life-dev/docs/orders.md +180 -0
- package/skills/active-life-dev/docs/patterns.md +319 -0
- package/skills/active-life-dev/docs/products.md +216 -0
- package/skills/active-life-dev/docs/schema.md +502 -0
- package/skills/active-life-dev/docs/setup.md +169 -0
- package/skills/active-life-dev/docs/vouchers.md +144 -0
- package/skills/ai-asset-generator/SKILL.md +247 -0
- package/skills/ai-asset-generator/docs/gen-image.md +274 -0
- package/skills/ai-asset-generator/docs/genvideo.md +341 -0
- package/skills/ai-asset-generator/docs/remove-background.md +19 -0
- package/skills/ai-asset-generator/lib/bg-remove.mjs +34 -0
- package/skills/ai-asset-generator/lib/env.mjs +48 -0
- package/skills/ai-asset-generator/lib/kie-client.mjs +100 -0
- package/skills/ai-build-ai/SKILL.md +127 -0
- package/skills/ai-build-ai/docs/agent-teams.md +293 -0
- package/skills/ai-build-ai/docs/checkpointing.md +161 -0
- package/skills/ai-build-ai/docs/create-agent.md +399 -0
- package/skills/ai-build-ai/docs/create-mcp.md +395 -0
- package/skills/ai-build-ai/docs/create-skill.md +299 -0
- package/skills/ai-build-ai/docs/headless-mode.md +614 -0
- package/skills/ai-build-ai/docs/hooks.md +578 -0
- package/skills/ai-build-ai/docs/memory-claude-md.md +375 -0
- package/skills/ai-build-ai/docs/output-styles.md +208 -0
- package/skills/ai-build-ai/docs/overview.md +162 -0
- package/skills/ai-build-ai/docs/permissions.md +391 -0
- package/skills/ai-build-ai/docs/plugins.md +396 -0
- package/skills/ai-build-ai/docs/sandbox.md +262 -0
- package/skills/ai-build-ai/docs/team-lead-workflow.md +648 -0
- package/skills/ant-design/SKILL.md +323 -0
- package/skills/ant-design/docs/components.md +160 -0
- package/skills/ant-design/docs/data-entry.md +406 -0
- package/skills/ant-design/docs/display.md +594 -0
- package/skills/ant-design/docs/feedback.md +451 -0
- package/skills/ant-design/docs/key-components.md +414 -0
- package/skills/ant-design/docs/navigation.md +310 -0
- package/skills/ant-design/docs/pro-components.md +543 -0
- package/skills/ant-design/docs/setup.md +213 -0
- package/skills/ant-design/docs/theme.md +265 -0
- package/skills/flutter-performance/SKILL.md +803 -0
- package/skills/flutter-performance/references/flutter-patterns.md +595 -0
- package/skills/icon-generator/SKILL.md +270 -0
- package/skills/mobile-app-review/SKILL.md +321 -0
- package/skills/mobile-app-review/references/apple-review.md +132 -0
- package/skills/mobile-app-review/references/google-play-review.md +203 -0
- package/skills/mongodb/SKILL.md +667 -0
- package/skills/mongodb/references/mongoose-patterns.md +368 -0
- package/skills/nestjs-architecture/SKILL.md +1086 -0
- package/skills/nestjs-architecture/references/advanced-patterns.md +590 -0
- package/skills/performance/SKILL.md +509 -0
- package/skills/react-fsd-architecture/SKILL.md +693 -0
- package/skills/react-fsd-architecture/references/fsd-patterns.md +747 -0
- package/skills/react-native-expo/SKILL.md +128 -0
- package/skills/react-native-expo/references/data-layer.md +252 -0
- package/skills/react-native-expo/references/design-system.md +252 -0
- package/skills/react-native-expo/references/navigation.md +199 -0
- package/skills/react-native-expo/references/performance.md +229 -0
- package/skills/react-native-expo/references/platform-services.md +179 -0
- package/skills/react-native-expo/references/state-management.md +209 -0
- package/skills/react-native-expo/references/ui-patterns.md +301 -0
- package/skills/react-query/SKILL.md +685 -0
- package/skills/react-query/references/query-patterns.md +365 -0
- package/skills/review-code/SKILL.md +374 -0
- package/skills/review-code/references/clean-code-principles.md +395 -0
- package/skills/review-code/references/frontend-patterns.md +136 -0
- package/skills/review-code/references/nestjs-patterns.md +184 -0
- package/skills/security-scanner/SKILL.md +366 -0
- package/skills/security-scanner/references/nestjs-security.md +260 -0
- package/skills/security-scanner/references/nextjs-security.md +201 -0
- package/skills/security-scanner/references/react-native-security.md +199 -0
- package/skills/traefik/SKILL.md +105 -0
- package/skills/traefik/docs/advanced-routing.md +186 -0
- package/skills/traefik/docs/auth-providers.md +137 -0
- package/skills/traefik/docs/cicd-devops.md +396 -0
- package/skills/traefik/docs/core-config.md +171 -0
- package/skills/traefik/docs/distributed-config.md +96 -0
- package/skills/traefik/docs/docker-compose.md +182 -0
- package/skills/traefik/docs/ha-performance.md +177 -0
- package/skills/traefik/docs/kubernetes.md +278 -0
- package/skills/traefik/docs/middleware.md +205 -0
- package/skills/traefik/docs/monitoring.md +357 -0
- package/skills/traefik/docs/security.md +391 -0
- package/skills/traefik/docs/tls-acme.md +155 -0
- package/skills/ui-ux-pro-max/SKILL.md +377 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
# Advanced React Query Patterns
|
|
2
|
+
|
|
3
|
+
Detailed reference for advanced patterns beyond the main SKILL.md. Load this when the user is working on specific advanced scenarios.
|
|
4
|
+
|
|
5
|
+
## Parallel Queries
|
|
6
|
+
|
|
7
|
+
### Independent queries in a component
|
|
8
|
+
|
|
9
|
+
Simply call multiple `useQuery` hooks — React Query fetches them in parallel:
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
const { data: users } = useUsers()
|
|
13
|
+
const { data: projects } = useProjects()
|
|
14
|
+
const { data: notifications } = useNotifications()
|
|
15
|
+
// All three fire simultaneously
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Dynamic parallel queries with `useQueries`
|
|
19
|
+
|
|
20
|
+
When the number of queries is dynamic (e.g., fetch details for a list of IDs):
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
const todoQueries = useQueries({
|
|
24
|
+
queries: todoIds.map(id => ({
|
|
25
|
+
queryKey: todoKeys.detail(id),
|
|
26
|
+
queryFn: () => fetchTodoById(id),
|
|
27
|
+
staleTime: 1000 * 60 * 5,
|
|
28
|
+
})),
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const allLoaded = todoQueries.every(q => q.isSuccess)
|
|
32
|
+
const allData = todoQueries.map(q => q.data).filter(Boolean)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Combine results with `combine`
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
const todoQueries = useQueries({
|
|
39
|
+
queries: todoIds.map(id => ({
|
|
40
|
+
queryKey: todoKeys.detail(id),
|
|
41
|
+
queryFn: () => fetchTodoById(id),
|
|
42
|
+
})),
|
|
43
|
+
combine: (results) => ({
|
|
44
|
+
data: results.map(r => r.data).filter(Boolean),
|
|
45
|
+
isLoading: results.some(r => r.isLoading),
|
|
46
|
+
isError: results.some(r => r.isError),
|
|
47
|
+
}),
|
|
48
|
+
})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Polling / Auto-Refetch
|
|
52
|
+
|
|
53
|
+
### Interval-based polling
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
const { data } = useQuery({
|
|
57
|
+
queryKey: ['job-status', jobId],
|
|
58
|
+
queryFn: () => fetchJobStatus(jobId),
|
|
59
|
+
refetchInterval: 3000, // poll every 3 seconds
|
|
60
|
+
refetchIntervalInBackground: false, // stop when tab is hidden
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Conditional polling (stop when done)
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const { data } = useQuery({
|
|
68
|
+
queryKey: ['job-status', jobId],
|
|
69
|
+
queryFn: () => fetchJobStatus(jobId),
|
|
70
|
+
refetchInterval: (query) => {
|
|
71
|
+
return query.state.data?.status === 'completed' ? false : 3000
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Pagination
|
|
77
|
+
|
|
78
|
+
### Basic pagination with placeholderData
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { keepPreviousData } from '@tanstack/react-query'
|
|
82
|
+
|
|
83
|
+
const [page, setPage] = useState(1)
|
|
84
|
+
|
|
85
|
+
const { data, isPlaceholderData } = useQuery({
|
|
86
|
+
queryKey: ['todos', page],
|
|
87
|
+
queryFn: () => fetchTodos(page),
|
|
88
|
+
placeholderData: keepPreviousData, // keep showing old data while new page loads
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
<Button
|
|
92
|
+
disabled={isPlaceholderData || !data?.hasNextPage}
|
|
93
|
+
onPress={() => setPage(p => p + 1)}
|
|
94
|
+
/>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Prefetch next page
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
if (data?.hasNextPage) {
|
|
102
|
+
queryClient.prefetchQuery({
|
|
103
|
+
queryKey: ['todos', page + 1],
|
|
104
|
+
queryFn: () => fetchTodos(page + 1),
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
}, [data, page])
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Cursor-Based Infinite Queries
|
|
111
|
+
|
|
112
|
+
### Bi-directional infinite query
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const {
|
|
116
|
+
data,
|
|
117
|
+
fetchNextPage,
|
|
118
|
+
fetchPreviousPage,
|
|
119
|
+
hasNextPage,
|
|
120
|
+
hasPreviousPage,
|
|
121
|
+
} = useInfiniteQuery({
|
|
122
|
+
queryKey: ['messages', chatId],
|
|
123
|
+
queryFn: ({ pageParam }) => fetchMessages(chatId, pageParam),
|
|
124
|
+
initialPageParam: { cursor: undefined, direction: 'forward' },
|
|
125
|
+
getNextPageParam: (lastPage) =>
|
|
126
|
+
lastPage.nextCursor ? { cursor: lastPage.nextCursor, direction: 'forward' } : undefined,
|
|
127
|
+
getPreviousPageParam: (firstPage) =>
|
|
128
|
+
firstPage.prevCursor ? { cursor: firstPage.prevCursor, direction: 'backward' } : undefined,
|
|
129
|
+
maxPages: 20, // limit stored pages
|
|
130
|
+
})
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## SSR / Hydration (Next.js)
|
|
134
|
+
|
|
135
|
+
### Prefetch on server, hydrate on client
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// app/todos/page.tsx (Server Component)
|
|
139
|
+
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'
|
|
140
|
+
|
|
141
|
+
export default async function TodosPage() {
|
|
142
|
+
const queryClient = new QueryClient()
|
|
143
|
+
|
|
144
|
+
await queryClient.prefetchQuery({
|
|
145
|
+
queryKey: todoKeys.lists(),
|
|
146
|
+
queryFn: fetchTodos,
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<HydrationBoundary state={dehydrate(queryClient)}>
|
|
151
|
+
<TodoList />
|
|
152
|
+
</HydrationBoundary>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// components/TodoList.tsx (Client Component)
|
|
157
|
+
'use client'
|
|
158
|
+
|
|
159
|
+
export function TodoList() {
|
|
160
|
+
// This will use the prefetched data — no loading state on first render
|
|
161
|
+
const { data } = useTodos()
|
|
162
|
+
return <div>{data?.map(todo => ...)}</div>
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Placeholder & Initial Data
|
|
167
|
+
|
|
168
|
+
### initialData — counted as "real" data
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
// Pre-fill detail from list cache
|
|
172
|
+
useQuery({
|
|
173
|
+
queryKey: todoKeys.detail(id),
|
|
174
|
+
queryFn: () => fetchTodoById(id),
|
|
175
|
+
initialData: () => {
|
|
176
|
+
return queryClient
|
|
177
|
+
.getQueryData<Todo[]>(todoKeys.lists())
|
|
178
|
+
?.find(t => t.id === id)
|
|
179
|
+
},
|
|
180
|
+
initialDataUpdatedAt: () => {
|
|
181
|
+
return queryClient.getQueryState(todoKeys.lists())?.dataUpdatedAt
|
|
182
|
+
},
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### placeholderData — shown while loading, not cached
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
useQuery({
|
|
190
|
+
queryKey: todoKeys.detail(id),
|
|
191
|
+
queryFn: () => fetchTodoById(id),
|
|
192
|
+
placeholderData: { id, title: 'Loading...', completed: false },
|
|
193
|
+
})
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Key difference:
|
|
197
|
+
- `initialData` is persisted in cache, respects `staleTime`, counts as "real"
|
|
198
|
+
- `placeholderData` is temporary, always triggers a fetch, never cached
|
|
199
|
+
|
|
200
|
+
## Request Deduplication
|
|
201
|
+
|
|
202
|
+
React Query automatically dedupes identical queries. If 10 components call `useTodos()`, only one network request fires. All 10 share the same cache entry and re-render together.
|
|
203
|
+
|
|
204
|
+
This is automatic — no configuration needed. Just use the same query key.
|
|
205
|
+
|
|
206
|
+
## Retry & Backoff
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
const queryClient = new QueryClient({
|
|
210
|
+
defaultOptions: {
|
|
211
|
+
queries: {
|
|
212
|
+
retry: 3, // retry 3 times (default)
|
|
213
|
+
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), // exponential backoff
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
// Disable retry for specific queries
|
|
219
|
+
useQuery({
|
|
220
|
+
queryKey: ['login'],
|
|
221
|
+
queryFn: login,
|
|
222
|
+
retry: false, // don't retry auth failures
|
|
223
|
+
})
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Query Cancellation
|
|
227
|
+
|
|
228
|
+
React Query automatically cancels queries when components unmount using `AbortSignal`:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
const { data } = useQuery({
|
|
232
|
+
queryKey: ['todos'],
|
|
233
|
+
queryFn: ({ signal }) => {
|
|
234
|
+
return fetch('/api/todos', { signal }).then(r => r.json())
|
|
235
|
+
},
|
|
236
|
+
})
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
For Axios:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
queryFn: ({ signal }) => {
|
|
243
|
+
return axios.get('/api/todos', { signal }).then(r => r.data)
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Mutation Side Effects Chain
|
|
248
|
+
|
|
249
|
+
The full lifecycle of a mutation:
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
mutate() called
|
|
253
|
+
→ onMutate (optimistic update, return rollback context)
|
|
254
|
+
→ mutationFn (actual API call)
|
|
255
|
+
→ onSuccess / onError
|
|
256
|
+
→ onSettled (runs regardless of success/error)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Coordinating mutation callbacks
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// Hook-level callbacks (always run)
|
|
263
|
+
useMutation({
|
|
264
|
+
mutationFn: updateTodo,
|
|
265
|
+
onSuccess: () => { /* runs for every call */ },
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
// Call-site callbacks (run for this specific call only)
|
|
269
|
+
mutation.mutate(data, {
|
|
270
|
+
onSuccess: () => { /* runs only for this call */ },
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
// Execution order: hook onSuccess → call-site onSuccess
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Testing React Query
|
|
277
|
+
|
|
278
|
+
### Setup test wrapper
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
const createWrapper = () => {
|
|
282
|
+
const queryClient = new QueryClient({
|
|
283
|
+
defaultOptions: {
|
|
284
|
+
queries: {
|
|
285
|
+
retry: false, // don't retry in tests
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
return ({ children }) => (
|
|
291
|
+
<QueryClientProvider client={queryClient}>
|
|
292
|
+
{children}
|
|
293
|
+
</QueryClientProvider>
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Test a custom hook
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
import { renderHook, waitFor } from '@testing-library/react'
|
|
302
|
+
|
|
303
|
+
it('fetches todos', async () => {
|
|
304
|
+
const { result } = renderHook(() => useTodos(), {
|
|
305
|
+
wrapper: createWrapper(),
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
|
309
|
+
|
|
310
|
+
expect(result.current.data).toHaveLength(3)
|
|
311
|
+
})
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Mock at the network level
|
|
315
|
+
|
|
316
|
+
Prefer MSW (Mock Service Worker) over mocking `queryFn` directly — it tests the full request pipeline:
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
import { http, HttpResponse } from 'msw'
|
|
320
|
+
import { setupServer } from 'msw/node'
|
|
321
|
+
|
|
322
|
+
const server = setupServer(
|
|
323
|
+
http.get('/api/todos', () => {
|
|
324
|
+
return HttpResponse.json([
|
|
325
|
+
{ id: 1, title: 'Test', completed: false },
|
|
326
|
+
])
|
|
327
|
+
})
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
beforeAll(() => server.listen())
|
|
331
|
+
afterEach(() => server.resetHandlers())
|
|
332
|
+
afterAll(() => server.close())
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Performance Optimization
|
|
336
|
+
|
|
337
|
+
### Tracked queries (v5 default)
|
|
338
|
+
|
|
339
|
+
React Query v5 tracks which fields you access from the query result. If you only use `data`, the component won't re-render when `isFetching` changes. This is automatic.
|
|
340
|
+
|
|
341
|
+
### `notifyOnChangeProps`
|
|
342
|
+
|
|
343
|
+
For fine-grained control:
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
useQuery({
|
|
347
|
+
queryKey: ['todos'],
|
|
348
|
+
queryFn: fetchTodos,
|
|
349
|
+
notifyOnChangeProps: ['data', 'error'], // ignore isFetching changes
|
|
350
|
+
})
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### `structuralSharing`
|
|
354
|
+
|
|
355
|
+
Enabled by default. React Query compares old and new data structurally and reuses unchanged references, preventing unnecessary re-renders in components that depend on specific parts of the data.
|
|
356
|
+
|
|
357
|
+
Disable only for very large datasets where comparison is expensive:
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
useQuery({
|
|
361
|
+
queryKey: ['huge-data'],
|
|
362
|
+
queryFn: fetchHugeData,
|
|
363
|
+
structuralSharing: false,
|
|
364
|
+
})
|
|
365
|
+
```
|