@codeleap/query 5.8.3 → 5.8.5
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 +6 -10
- package/package.json.bak +3 -7
- package/src/factors/createQueryManager.ts +38 -0
- package/src/factors/createQueryOperations.ts +37 -0
- package/src/factors/index.ts +2 -0
- package/src/index.ts +2 -8
- package/src/lib/Mutations.ts +280 -0
- package/src/{queryClient.ts → lib/QueryClientEnhanced/index.ts} +24 -72
- package/src/lib/QueryClientEnhanced/types.ts +38 -0
- package/src/lib/QueryKeys.ts +319 -0
- package/src/lib/QueryManager.ts +488 -0
- package/src/lib/QueryOperations/index.ts +351 -0
- package/src/lib/QueryOperations/types.ts +47 -0
- package/src/lib/index.ts +5 -0
- package/src/tests/Mutations.spec.tsx +458 -0
- package/src/tests/QueryManager.spec.tsx +920 -0
- package/src/tests/QueryOperations.spec.tsx +109 -0
- package/src/tests/integration.spec.tsx +551 -0
- package/src/tests/setup.ts +119 -0
- package/src/types/core.ts +33 -0
- package/src/types/create.ts +16 -0
- package/src/types/delete.ts +15 -0
- package/src/types/index.ts +7 -0
- package/src/types/list.ts +24 -0
- package/src/types/retrieve.ts +7 -0
- package/src/types/update.ts +14 -0
- package/src/types/utility.ts +22 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/misc.ts +43 -0
- package/src/QueryManager.ts +0 -954
- package/src/types.ts +0 -199
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import { FetchQueryOptions, InfiniteData, MutationFunctionContext, QueryKey, useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query'
|
|
2
|
+
import { useCallback } from 'react'
|
|
3
|
+
import { createQueryKeys, QueryKeys } from './QueryKeys'
|
|
4
|
+
import { createMutations, Mutations } from './Mutations'
|
|
5
|
+
import { CreateMutationCtx, CreateMutationOptions, ListPaginationResponse, ListQueryOptions, PageParam, QueryItem, QueryManagerOptions, RetrieveQueryOptions, UpdateMutationCtx, UpdateMutationOptions, DeleteMutationCtx, DeleteMutationOptions } from '../types'
|
|
6
|
+
import { generateTempId } from '../utils'
|
|
7
|
+
import { TypeGuards } from '@codeleap/types'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Comprehensive query manager class that provides hooks and utilities for managing CRUD operations with React Query
|
|
11
|
+
* @template T - The query item type that extends QueryItem
|
|
12
|
+
* @template F - The filter type used for list queries
|
|
13
|
+
*
|
|
14
|
+
* @description
|
|
15
|
+
* QueryManager provides a complete solution for managing list and individual item queries with:
|
|
16
|
+
* - Infinite scroll pagination for lists
|
|
17
|
+
* - Optimistic updates for create, update, and delete operations
|
|
18
|
+
* - Automatic cache synchronization between list and individual queries
|
|
19
|
+
* - Built-in error handling and rollback mechanisms
|
|
20
|
+
*/
|
|
21
|
+
export class QueryManager<T extends QueryItem, F> {
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new QueryManager instance
|
|
24
|
+
* @param options - Configuration options for the query manager
|
|
25
|
+
*/
|
|
26
|
+
constructor(private options: QueryManagerOptions<T, F>) {
|
|
27
|
+
this.queryKeys = createQueryKeys<T, F>(options.name, options.queryClient)
|
|
28
|
+
this.mutations = createMutations<T, F>(this.queryKeys, options.queryClient, options.name)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Gets the name of this query manager
|
|
33
|
+
* @returns The query name used for identification
|
|
34
|
+
*/
|
|
35
|
+
get name() {
|
|
36
|
+
return this.options.name
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Gets the configured CRUD functions
|
|
41
|
+
* @returns Object containing all configured function handlers
|
|
42
|
+
*/
|
|
43
|
+
get functions() {
|
|
44
|
+
return {
|
|
45
|
+
list: this.options.listFn,
|
|
46
|
+
retrieve: this.options.retrieveFn,
|
|
47
|
+
create: this.options.createFn,
|
|
48
|
+
update: this.options.updateFn,
|
|
49
|
+
delete: this.options.deleteFn,
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** QueryKeys instance for managing query keys and cache operations */
|
|
54
|
+
queryKeys: QueryKeys<T, F>
|
|
55
|
+
|
|
56
|
+
/** Mutations instance for managing cache mutations */
|
|
57
|
+
mutations: Mutations<T, F>
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* React hook for infinite scroll list queries with pagination
|
|
61
|
+
* @param options - Configuration options for the list query
|
|
62
|
+
* @returns Object containing items array, query key, and query object
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const { items, query } = queryManager.useList({
|
|
67
|
+
* filters: { status: 'active' },
|
|
68
|
+
* limit: 20
|
|
69
|
+
* })
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
useList(options: ListQueryOptions<T, F> = {}) {
|
|
73
|
+
const {
|
|
74
|
+
limit,
|
|
75
|
+
filters,
|
|
76
|
+
...queryOptions
|
|
77
|
+
} = options
|
|
78
|
+
|
|
79
|
+
const listLimit = limit ?? this.options?.listLimit ?? 10
|
|
80
|
+
|
|
81
|
+
const queryKey = this.queryKeys.useListKeyWithFilters(filters)
|
|
82
|
+
|
|
83
|
+
const onSelect = useCallback((data: InfiniteData<ListPaginationResponse<T>, PageParam>) => {
|
|
84
|
+
const pages = data?.pages ?? []
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
pageParams: data?.pageParams,
|
|
88
|
+
pages,
|
|
89
|
+
allItems: pages.flat(),
|
|
90
|
+
}
|
|
91
|
+
}, [])
|
|
92
|
+
|
|
93
|
+
const query = useInfiniteQuery({
|
|
94
|
+
queryKey,
|
|
95
|
+
|
|
96
|
+
queryFn: async (query) => {
|
|
97
|
+
const listOffset = query?.pageParam ?? 0
|
|
98
|
+
|
|
99
|
+
return this.options?.listFn?.(listLimit, listOffset, filters)
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
initialPageParam: 0,
|
|
103
|
+
|
|
104
|
+
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => {
|
|
105
|
+
if (!lastPage?.length || lastPage.length < listLimit) {
|
|
106
|
+
return undefined
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return (lastPageParam ?? 0) + lastPage.length
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
getPreviousPageParam: (firstPage, allPages, firstPageParam, allPageParams) => {
|
|
113
|
+
if ((firstPageParam ?? 0) <= 0) {
|
|
114
|
+
return undefined
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return (firstPageParam ?? 0) - listLimit
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
select(data) {
|
|
121
|
+
return onSelect(data)
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
...queryOptions,
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
const useListEffect = (this.options.useListEffect ?? ((args: any) => null))
|
|
128
|
+
|
|
129
|
+
useListEffect(query)
|
|
130
|
+
|
|
131
|
+
const items = query.data?.allItems ?? []
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
items,
|
|
135
|
+
queryKey,
|
|
136
|
+
query,
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* React hook for retrieving a single item by ID
|
|
142
|
+
* @param id - The ID of the item to retrieve
|
|
143
|
+
* @param options - Configuration options for the retrieve query
|
|
144
|
+
* @returns Object containing the item data, query key, and query object
|
|
145
|
+
*
|
|
146
|
+
* @description
|
|
147
|
+
* This hook automatically:
|
|
148
|
+
* - Uses list cache as initial data if available
|
|
149
|
+
* - Updates list cache when retrieve data changes
|
|
150
|
+
* - Synchronizes data between list and individual caches
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const { item, query } = queryManager.useRetrieve('user-123', {
|
|
155
|
+
* enabled: !!userId
|
|
156
|
+
* })
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
useRetrieve(id: T['id'], options: RetrieveQueryOptions<T> = {}) {
|
|
160
|
+
const {
|
|
161
|
+
select,
|
|
162
|
+
...queryOptions
|
|
163
|
+
} = options
|
|
164
|
+
|
|
165
|
+
const onSelect = useCallback((data: T) => {
|
|
166
|
+
this.mutations.updateItems(data)
|
|
167
|
+
if (select) select?.(data)
|
|
168
|
+
return data
|
|
169
|
+
}, [select])
|
|
170
|
+
|
|
171
|
+
const getInitialData = useCallback(() => {
|
|
172
|
+
const { itemMap } = this.queryKeys.getListData()
|
|
173
|
+
return itemMap?.[id]
|
|
174
|
+
}, [id])
|
|
175
|
+
|
|
176
|
+
const queryKey = this.queryKeys.useRetrieveKey(id)
|
|
177
|
+
|
|
178
|
+
const query = useQuery({
|
|
179
|
+
initialData: getInitialData,
|
|
180
|
+
|
|
181
|
+
...queryOptions,
|
|
182
|
+
|
|
183
|
+
queryKey,
|
|
184
|
+
|
|
185
|
+
queryFn: () => {
|
|
186
|
+
return this.options.retrieveFn(id)
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
select: (data) => {
|
|
190
|
+
return onSelect(data)
|
|
191
|
+
},
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
item: query.data,
|
|
196
|
+
queryKey,
|
|
197
|
+
query,
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* React hook for creating new items with optimistic updates
|
|
203
|
+
* @param options - Configuration options for the create mutation
|
|
204
|
+
* @returns React Query mutation object for create operations
|
|
205
|
+
*
|
|
206
|
+
* @description
|
|
207
|
+
* This hook supports optimistic updates by:
|
|
208
|
+
* - Immediately adding a temporary item to the cache
|
|
209
|
+
* - Rolling back on error by removing the temporary item
|
|
210
|
+
* - Replacing temporary item with real data on success
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```typescript
|
|
214
|
+
* const createMutation = queryManager.useCreate({
|
|
215
|
+
* optimistic: true,
|
|
216
|
+
* appendTo: 'start',
|
|
217
|
+
* listFilters: { status: 'active' }
|
|
218
|
+
* })
|
|
219
|
+
*
|
|
220
|
+
* // Usage
|
|
221
|
+
* createMutation.mutate({ name: 'New User', email: 'user@example.com' })
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
useCreate(options: CreateMutationOptions<T, F> = {}) {
|
|
225
|
+
const {
|
|
226
|
+
optimistic,
|
|
227
|
+
listFilters,
|
|
228
|
+
appendTo,
|
|
229
|
+
onMutate: providedOnMutate,
|
|
230
|
+
onError: providedOnError,
|
|
231
|
+
onSuccess: providedOnSuccess,
|
|
232
|
+
...mutationOptions
|
|
233
|
+
} = options
|
|
234
|
+
|
|
235
|
+
const onMutate = useCallback(async (data: Partial<T>, context: MutationFunctionContext) => {
|
|
236
|
+
if (providedOnMutate) providedOnMutate?.(data, context)
|
|
237
|
+
|
|
238
|
+
if (optimistic) {
|
|
239
|
+
await this.queryKeys.cancelListQueries(listFilters)
|
|
240
|
+
|
|
241
|
+
const tempId = generateTempId()
|
|
242
|
+
|
|
243
|
+
const newItem = { ...data, id: tempId } as T
|
|
244
|
+
|
|
245
|
+
this.mutations.addItem(newItem, appendTo, listFilters)
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
tempId,
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}, [providedOnMutate, optimistic, listFilters])
|
|
252
|
+
|
|
253
|
+
const onError = useCallback((error: Error, variables: Partial<T>, onMutateResult: CreateMutationCtx, context: MutationFunctionContext) => {
|
|
254
|
+
if (providedOnError) providedOnError?.(error, variables, onMutateResult, context)
|
|
255
|
+
|
|
256
|
+
if (!TypeGuards.isNil(onMutateResult?.tempId)) {
|
|
257
|
+
this.mutations.removeItem(onMutateResult?.tempId)
|
|
258
|
+
}
|
|
259
|
+
}, [providedOnError])
|
|
260
|
+
|
|
261
|
+
const onSuccess = useCallback((data: T, variables: Partial<T>, onMutateResult: CreateMutationCtx, context: MutationFunctionContext) => {
|
|
262
|
+
if (providedOnSuccess) providedOnSuccess?.(data, variables, onMutateResult, context)
|
|
263
|
+
|
|
264
|
+
if (TypeGuards.isNil(onMutateResult?.tempId)) {
|
|
265
|
+
this.mutations.addItem(data, appendTo, listFilters)
|
|
266
|
+
} else {
|
|
267
|
+
this.mutations.updateItems({ ...data, tempId: onMutateResult?.tempId })
|
|
268
|
+
}
|
|
269
|
+
}, [providedOnSuccess, listFilters])
|
|
270
|
+
|
|
271
|
+
const mutation = useMutation<T, Error, Partial<T>, CreateMutationCtx>({
|
|
272
|
+
...mutationOptions,
|
|
273
|
+
|
|
274
|
+
mutationKey: this.queryKeys.keys.create,
|
|
275
|
+
|
|
276
|
+
mutationFn: (data: Partial<T>) => {
|
|
277
|
+
return this.options.createFn(data)
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
onMutate,
|
|
281
|
+
|
|
282
|
+
onError,
|
|
283
|
+
|
|
284
|
+
onSuccess,
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
return mutation
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* React hook for updating existing items with optimistic updates
|
|
292
|
+
* @param options - Configuration options for the update mutation
|
|
293
|
+
* @returns React Query mutation object for update operations
|
|
294
|
+
*
|
|
295
|
+
* @description
|
|
296
|
+
* This hook supports optimistic updates by:
|
|
297
|
+
* - Immediately updating the item in cache with new data
|
|
298
|
+
* - Rolling back to previous data on error
|
|
299
|
+
* - Confirming updates with server response on success
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* ```typescript
|
|
303
|
+
* const updateMutation = queryManager.useUpdate({
|
|
304
|
+
* optimistic: true,
|
|
305
|
+
* onSuccess: (data) => console.log('Updated:', data)
|
|
306
|
+
* })
|
|
307
|
+
*
|
|
308
|
+
* // Usage
|
|
309
|
+
* updateMutation.mutate({ id: 'user-123', name: 'Updated Name' })
|
|
310
|
+
* ```
|
|
311
|
+
*/
|
|
312
|
+
useUpdate(options: UpdateMutationOptions<T, F> = {}) {
|
|
313
|
+
const {
|
|
314
|
+
optimistic,
|
|
315
|
+
onMutate: providedOnMutate,
|
|
316
|
+
onError: providedOnError,
|
|
317
|
+
onSuccess: providedOnSuccess,
|
|
318
|
+
...mutationOptions
|
|
319
|
+
} = options
|
|
320
|
+
|
|
321
|
+
const onMutate = useCallback(async (data: Partial<T>, context: MutationFunctionContext) => {
|
|
322
|
+
if (providedOnMutate) providedOnMutate?.(data, context)
|
|
323
|
+
|
|
324
|
+
if (optimistic) {
|
|
325
|
+
const previousItem = this.queryKeys.getRetrieveData(data?.id)
|
|
326
|
+
|
|
327
|
+
if (!previousItem) return
|
|
328
|
+
|
|
329
|
+
const optimisticItem = {
|
|
330
|
+
...previousItem,
|
|
331
|
+
...data,
|
|
332
|
+
} as T
|
|
333
|
+
|
|
334
|
+
this.mutations.updateItems(optimisticItem)
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
previousItem,
|
|
338
|
+
optimisticItem,
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}, [providedOnMutate, optimistic])
|
|
342
|
+
|
|
343
|
+
const onError = useCallback((error: Error, variables: Partial<T>, onMutateResult: UpdateMutationCtx<T>, context: MutationFunctionContext) => {
|
|
344
|
+
if (providedOnError) providedOnError?.(error, variables, onMutateResult, context)
|
|
345
|
+
|
|
346
|
+
if (!TypeGuards.isNil(onMutateResult?.previousItem?.id)) {
|
|
347
|
+
this.mutations.updateItems(onMutateResult?.previousItem)
|
|
348
|
+
}
|
|
349
|
+
}, [providedOnError])
|
|
350
|
+
|
|
351
|
+
const onSuccess = useCallback((data: T, variables: Partial<T>, onMutateResult: UpdateMutationCtx<T>, context: MutationFunctionContext) => {
|
|
352
|
+
if (providedOnSuccess) providedOnSuccess?.(data, variables, onMutateResult, context)
|
|
353
|
+
|
|
354
|
+
this.mutations.updateItems(data)
|
|
355
|
+
}, [providedOnSuccess])
|
|
356
|
+
|
|
357
|
+
const mutation = useMutation<T, Error, Partial<T>, UpdateMutationCtx<T>>({
|
|
358
|
+
...mutationOptions,
|
|
359
|
+
|
|
360
|
+
mutationKey: this.queryKeys.keys.update,
|
|
361
|
+
|
|
362
|
+
mutationFn: (data: Partial<T>) => {
|
|
363
|
+
return this.options.updateFn(data)
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
onMutate,
|
|
367
|
+
|
|
368
|
+
onError,
|
|
369
|
+
|
|
370
|
+
onSuccess,
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
return mutation
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* React hook for deleting items with optimistic updates
|
|
378
|
+
* @param options - Configuration options for the delete mutation
|
|
379
|
+
* @returns React Query mutation object for delete operations
|
|
380
|
+
*
|
|
381
|
+
* @description
|
|
382
|
+
* This hook supports optimistic updates by:
|
|
383
|
+
* - Immediately removing the item from cache
|
|
384
|
+
* - Storing the removal positions for potential rollback
|
|
385
|
+
* - Restoring the item to original positions on error
|
|
386
|
+
* - Confirming deletion on success
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* ```typescript
|
|
390
|
+
* const deleteMutation = queryManager.useDelete({
|
|
391
|
+
* optimistic: true,
|
|
392
|
+
* onSuccess: () => console.log('Item deleted successfully')
|
|
393
|
+
* })
|
|
394
|
+
*
|
|
395
|
+
* // Usage
|
|
396
|
+
* deleteMutation.mutate('user-123')
|
|
397
|
+
* ```
|
|
398
|
+
*/
|
|
399
|
+
useDelete(options: DeleteMutationOptions<T, F> = {}) {
|
|
400
|
+
const {
|
|
401
|
+
optimistic,
|
|
402
|
+
onMutate: providedOnMutate,
|
|
403
|
+
onError: providedOnError,
|
|
404
|
+
onSuccess: providedOnSuccess,
|
|
405
|
+
...mutationOptions
|
|
406
|
+
} = options
|
|
407
|
+
|
|
408
|
+
const onMutate = useCallback(async (id: T['id'], context: MutationFunctionContext) => {
|
|
409
|
+
if (providedOnMutate) providedOnMutate?.(id, context)
|
|
410
|
+
|
|
411
|
+
if (optimistic) {
|
|
412
|
+
const previousItem = this.queryKeys.getRetrieveData(id)
|
|
413
|
+
|
|
414
|
+
if (!previousItem) return
|
|
415
|
+
|
|
416
|
+
const removedAt = this.mutations.removeItem(id)
|
|
417
|
+
|
|
418
|
+
return {
|
|
419
|
+
previousItem,
|
|
420
|
+
removedAt,
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}, [providedOnMutate, optimistic])
|
|
424
|
+
|
|
425
|
+
const onError = useCallback((error: Error, variables: T['id'], onMutateResult: DeleteMutationCtx<T>, context: MutationFunctionContext) => {
|
|
426
|
+
if (providedOnError) providedOnError?.(error, variables, onMutateResult, context)
|
|
427
|
+
|
|
428
|
+
if (!TypeGuards.isNil(onMutateResult?.previousItem?.id)) {
|
|
429
|
+
this.mutations.addItem(
|
|
430
|
+
onMutateResult?.previousItem,
|
|
431
|
+
onMutateResult?.removedAt,
|
|
432
|
+
)
|
|
433
|
+
}
|
|
434
|
+
}, [providedOnError])
|
|
435
|
+
|
|
436
|
+
const onSuccess = useCallback((data: T['id'], variables: T['id'], onMutateResult: DeleteMutationCtx<T>, context: MutationFunctionContext) => {
|
|
437
|
+
if (providedOnSuccess) providedOnSuccess?.(data, variables, onMutateResult, context)
|
|
438
|
+
|
|
439
|
+
if (TypeGuards.isNil(onMutateResult?.previousItem?.id)) {
|
|
440
|
+
this.mutations.removeItem(data)
|
|
441
|
+
}
|
|
442
|
+
}, [providedOnSuccess])
|
|
443
|
+
|
|
444
|
+
const mutation = useMutation<unknown, Error, T['id'], DeleteMutationCtx<T>>({
|
|
445
|
+
...mutationOptions,
|
|
446
|
+
|
|
447
|
+
mutationKey: this.queryKeys.keys.delete,
|
|
448
|
+
|
|
449
|
+
mutationFn: async (id: QueryItem['id']) => {
|
|
450
|
+
return this.options.deleteFn(id)
|
|
451
|
+
},
|
|
452
|
+
|
|
453
|
+
onMutate,
|
|
454
|
+
|
|
455
|
+
onError,
|
|
456
|
+
|
|
457
|
+
onSuccess,
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
return mutation
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Prefetches a single item by ID for improved performance
|
|
465
|
+
* @param id - The ID of the item to prefetch
|
|
466
|
+
* @param options - Prefetch options compatible with React Query
|
|
467
|
+
* @returns Promise that resolves when prefetch is complete
|
|
468
|
+
*
|
|
469
|
+
* @description
|
|
470
|
+
* Use this method to preload data that users are likely to need soon,
|
|
471
|
+
* such as when hovering over links or preparing for navigation.
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```typescript
|
|
475
|
+
* // Prefetch on hover
|
|
476
|
+
* const handleHover = (userId: string) => {
|
|
477
|
+
* queryManager.prefetchRetrieve(userId, { staleTime: 5 * 60 * 1000 })
|
|
478
|
+
* }
|
|
479
|
+
* ```
|
|
480
|
+
*/
|
|
481
|
+
prefetchRetrieve(id: T['id'], options: Omit<FetchQueryOptions<T, Error, T, QueryKey, never>, 'queryKey' | 'queryFn'> = {}) {
|
|
482
|
+
return this.options.queryClient.prefetchQuery({
|
|
483
|
+
...options,
|
|
484
|
+
queryKey: this.queryKeys.keys.retrieve(id),
|
|
485
|
+
queryFn: () => this.options.retrieveFn(id),
|
|
486
|
+
})
|
|
487
|
+
}
|
|
488
|
+
}
|