@ic-reactor/react 3.2.0 → 3.3.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/README.md +58 -4
- package/dist/createActorHooks.d.ts +1 -1
- package/dist/createActorHooks.d.ts.map +1 -1
- package/dist/createActorHooks.js +10 -25
- package/dist/createActorHooks.js.map +1 -1
- package/dist/createInfiniteQuery.d.ts.map +1 -1
- package/dist/createInfiniteQuery.js +2 -12
- package/dist/createInfiniteQuery.js.map +1 -1
- package/dist/createMutation.d.ts.map +1 -1
- package/dist/createMutation.js +39 -56
- package/dist/createMutation.js.map +1 -1
- package/dist/createQuery.d.ts.map +1 -1
- package/dist/createQuery.js +31 -39
- package/dist/createQuery.js.map +1 -1
- package/dist/createSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/createSuspenseInfiniteQuery.js +1 -14
- package/dist/createSuspenseInfiniteQuery.js.map +1 -1
- package/dist/createSuspenseQuery.d.ts.map +1 -1
- package/dist/createSuspenseQuery.js +30 -39
- package/dist/createSuspenseQuery.js.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.js +4 -5
- package/dist/hooks/useActorInfiniteQuery.js.map +1 -1
- package/dist/hooks/useActorMutation.d.ts +9 -2
- package/dist/hooks/useActorMutation.d.ts.map +1 -1
- package/dist/hooks/useActorMutation.js +15 -15
- package/dist/hooks/useActorMutation.js.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.js +4 -5
- package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +1 -1
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +26 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +41 -0
- package/dist/utils.js.map +1 -0
- package/package.json +9 -9
- package/src/createActorHooks.ts +32 -31
- package/src/createInfiniteQuery.ts +2 -19
- package/src/createMutation.ts +52 -69
- package/src/createQuery.ts +41 -52
- package/src/createSuspenseInfiniteQuery.ts +1 -22
- package/src/createSuspenseQuery.ts +42 -56
- package/src/hooks/useActorInfiniteQuery.ts +4 -9
- package/src/hooks/useActorMutation.ts +40 -14
- package/src/hooks/useActorSuspenseInfiniteQuery.ts +4 -9
- package/src/types.ts +32 -0
- package/src/utils.ts +52 -0
package/src/createActorHooks.ts
CHANGED
|
@@ -33,18 +33,17 @@ import {
|
|
|
33
33
|
UseMutationResult,
|
|
34
34
|
InfiniteData,
|
|
35
35
|
} from "@tanstack/react-query"
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
|
|
41
|
-
SuspenseInfiniteQueryConfig,
|
|
42
|
-
} from "./createSuspenseInfiniteQuery"
|
|
43
|
-
import { createMutation } from "./createMutation"
|
|
36
|
+
import { useActorQuery } from "./hooks/useActorQuery"
|
|
37
|
+
import { useActorSuspenseQuery } from "./hooks/useActorSuspenseQuery"
|
|
38
|
+
import { useActorInfiniteQuery } from "./hooks/useActorInfiniteQuery"
|
|
39
|
+
import { useActorSuspenseInfiniteQuery } from "./hooks/useActorSuspenseInfiniteQuery"
|
|
40
|
+
import { useActorMutation } from "./hooks/useActorMutation"
|
|
44
41
|
import {
|
|
45
42
|
useActorMethod,
|
|
46
43
|
UseActorMethodParameters,
|
|
47
44
|
} from "./hooks/useActorMethod"
|
|
45
|
+
import { InfiniteQueryConfig } from "./createInfiniteQuery"
|
|
46
|
+
import { SuspenseInfiniteQueryConfig } from "./createSuspenseInfiniteQuery"
|
|
48
47
|
import { QueryConfig, SuspenseQueryConfig, MutationConfig } from "./types"
|
|
49
48
|
|
|
50
49
|
export type ActorHooks<A, T extends TransformKey> = {
|
|
@@ -108,33 +107,35 @@ export function createActorHooks<A, T extends TransformKey>(
|
|
|
108
107
|
reactor: Reactor<A, T>
|
|
109
108
|
): ActorHooks<A, T> {
|
|
110
109
|
return {
|
|
111
|
-
useActorQuery: ((config: any) =>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
useActorQuery: ((config: any) =>
|
|
111
|
+
useActorQuery({ ...config, reactor })) as ActorHooks<
|
|
112
|
+
A,
|
|
113
|
+
T
|
|
114
|
+
>["useActorQuery"],
|
|
115
115
|
|
|
116
|
-
useActorSuspenseQuery: ((config: any) =>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
useActorSuspenseQuery: ((config: any) =>
|
|
117
|
+
useActorSuspenseQuery({ ...config, reactor })) as ActorHooks<
|
|
118
|
+
A,
|
|
119
|
+
T
|
|
120
|
+
>["useActorSuspenseQuery"],
|
|
120
121
|
|
|
121
|
-
useActorInfiniteQuery: ((config) =>
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
useActorInfiniteQuery: ((config: any) =>
|
|
123
|
+
useActorInfiniteQuery({ ...config, reactor })) as ActorHooks<
|
|
124
|
+
A,
|
|
125
|
+
T
|
|
126
|
+
>["useActorInfiniteQuery"],
|
|
125
127
|
|
|
126
|
-
useActorSuspenseInfiniteQuery: ((config) =>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
).useSuspenseInfiniteQuery(options)
|
|
132
|
-
}) as ActorHooks<A, T>["useActorSuspenseInfiniteQuery"],
|
|
128
|
+
useActorSuspenseInfiniteQuery: ((config: any) =>
|
|
129
|
+
useActorSuspenseInfiniteQuery({ ...config, reactor })) as ActorHooks<
|
|
130
|
+
A,
|
|
131
|
+
T
|
|
132
|
+
>["useActorSuspenseInfiniteQuery"],
|
|
133
133
|
|
|
134
|
-
useActorMutation: ((config) =>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
useActorMutation: ((config: any) =>
|
|
135
|
+
useActorMutation({ ...config, reactor })) as ActorHooks<
|
|
136
|
+
A,
|
|
137
|
+
T
|
|
138
|
+
>["useActorMutation"],
|
|
138
139
|
|
|
139
140
|
useActorMethod: (config) =>
|
|
140
141
|
useActorMethod({ ...config, reactor } as UseActorMethodParameters<
|
|
@@ -43,8 +43,7 @@ import {
|
|
|
43
43
|
} from "@tanstack/react-query"
|
|
44
44
|
import { CallConfig } from "@icp-sdk/core/agent"
|
|
45
45
|
import { NoInfer } from "./types"
|
|
46
|
-
|
|
47
|
-
const FACTORY_KEY_ARGS_QUERY_KEY = "__ic_reactor_factory_key_args"
|
|
46
|
+
import { mergeFactoryQueryKey } from "./utils"
|
|
48
47
|
|
|
49
48
|
type InfiniteQueryFactoryFn<
|
|
50
49
|
A,
|
|
@@ -63,22 +62,6 @@ type InfiniteQueryFactoryFn<
|
|
|
63
62
|
>
|
|
64
63
|
}
|
|
65
64
|
|
|
66
|
-
const mergeFactoryQueryKey = (
|
|
67
|
-
baseQueryKey?: QueryKey,
|
|
68
|
-
keyArgs?: unknown
|
|
69
|
-
): QueryKey | undefined => {
|
|
70
|
-
const merged: unknown[] = []
|
|
71
|
-
|
|
72
|
-
if (baseQueryKey) {
|
|
73
|
-
merged.push(...baseQueryKey)
|
|
74
|
-
}
|
|
75
|
-
if (keyArgs !== undefined) {
|
|
76
|
-
merged.push({ [FACTORY_KEY_ARGS_QUERY_KEY]: keyArgs })
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return merged.length > 0 ? merged : undefined
|
|
80
|
-
}
|
|
81
|
-
|
|
82
65
|
// ============================================================================
|
|
83
66
|
// Type Definitions
|
|
84
67
|
// ============================================================================
|
|
@@ -510,7 +493,7 @@ export function createInfiniteQueryFactory<
|
|
|
510
493
|
) => {
|
|
511
494
|
const initialArgs = getArgs(config.initialPageParam)
|
|
512
495
|
const keyArgs = config.getKeyArgs?.(initialArgs) ?? initialArgs
|
|
513
|
-
const queryKey = mergeFactoryQueryKey(config.queryKey, keyArgs)
|
|
496
|
+
const queryKey = mergeFactoryQueryKey(config.queryKey, undefined, keyArgs)
|
|
514
497
|
|
|
515
498
|
return createInfiniteQueryImpl<A, M, T, TPageParam, TSelected>(reactor, {
|
|
516
499
|
...(({ getKeyArgs: _getKeyArgs, ...rest }) => rest)(
|
package/src/createMutation.ts
CHANGED
|
@@ -30,6 +30,22 @@ import type {
|
|
|
30
30
|
NoInfer,
|
|
31
31
|
} from "./types"
|
|
32
32
|
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// Internal helpers
|
|
35
|
+
// ============================================================================
|
|
36
|
+
|
|
37
|
+
/** Invalidate a list of query keys in parallel, filtering out undefineds. */
|
|
38
|
+
async function invalidateAll(
|
|
39
|
+
queryClient: Reactor<any, any>["queryClient"],
|
|
40
|
+
keys: (import("@tanstack/react-query").QueryKey | undefined)[]
|
|
41
|
+
): Promise<void> {
|
|
42
|
+
await Promise.all(
|
|
43
|
+
keys.map((queryKey) =>
|
|
44
|
+
queryKey ? queryClient.invalidateQueries({ queryKey }) : Promise.resolve()
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
33
49
|
// ============================================================================
|
|
34
50
|
// Internal Implementation
|
|
35
51
|
// ============================================================================
|
|
@@ -52,113 +68,80 @@ const createMutationImpl = <
|
|
|
52
68
|
...factoryOptions
|
|
53
69
|
} = config
|
|
54
70
|
|
|
55
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Raw call without any invalidation logic.
|
|
73
|
+
* Used as mutationFn so that onSuccess handles all post-mutation work
|
|
74
|
+
* and there is no double-invalidation.
|
|
75
|
+
*/
|
|
76
|
+
const callFn = (
|
|
77
|
+
args: ReactorArgs<A, M, T>
|
|
78
|
+
): Promise<ReactorReturnOk<A, M, T>> =>
|
|
79
|
+
reactor.callMethod({ functionName, args, callConfig })
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Imperative execution for non-React usage.
|
|
83
|
+
* Calls the canister method and invalidates factory-level queries.
|
|
84
|
+
* Use this in route loaders, scripts, or server-side code.
|
|
85
|
+
*/
|
|
56
86
|
const execute = async (
|
|
57
87
|
args: ReactorArgs<A, M, T>
|
|
58
88
|
): Promise<ReactorReturnOk<A, M, T>> => {
|
|
59
|
-
const result = await
|
|
60
|
-
functionName,
|
|
61
|
-
args,
|
|
62
|
-
callConfig,
|
|
63
|
-
})
|
|
64
|
-
|
|
89
|
+
const result = await callFn(args)
|
|
65
90
|
if (factoryInvalidateQueries) {
|
|
66
|
-
await
|
|
67
|
-
factoryInvalidateQueries.map((queryKey) => {
|
|
68
|
-
return reactor.queryClient.invalidateQueries({ queryKey })
|
|
69
|
-
})
|
|
70
|
-
)
|
|
91
|
+
await invalidateAll(reactor.queryClient, factoryInvalidateQueries)
|
|
71
92
|
}
|
|
72
|
-
|
|
73
93
|
return result
|
|
74
94
|
}
|
|
75
95
|
|
|
76
96
|
// Hook implementation
|
|
77
97
|
const useMutationHook = (options?: MutationHookOptions<A, M, T>) => {
|
|
78
|
-
const baseOptions = reactor.getQueryOptions({
|
|
79
|
-
functionName,
|
|
80
|
-
})
|
|
81
|
-
// Extract our custom options
|
|
98
|
+
const baseOptions = reactor.getQueryOptions({ functionName })
|
|
82
99
|
const {
|
|
83
100
|
invalidateQueries: hookInvalidateQueries,
|
|
84
101
|
onCanisterError: hookOnCanisterError,
|
|
85
102
|
...restOptions
|
|
86
|
-
} = options
|
|
103
|
+
} = options ?? {}
|
|
87
104
|
|
|
88
105
|
return useMutation(
|
|
89
106
|
{
|
|
90
107
|
mutationKey: baseOptions.queryKey,
|
|
91
108
|
...factoryOptions,
|
|
92
109
|
...restOptions,
|
|
93
|
-
|
|
110
|
+
// Use callFn (not execute) to avoid double-invalidation:
|
|
111
|
+
// factoryInvalidateQueries are handled in onSuccess below.
|
|
112
|
+
mutationFn: callFn,
|
|
94
113
|
onSuccess: async (...args) => {
|
|
95
|
-
// 1.
|
|
114
|
+
// 1. Factory-level invalidation
|
|
96
115
|
if (factoryInvalidateQueries) {
|
|
97
|
-
await
|
|
98
|
-
factoryInvalidateQueries.map((queryKey) => {
|
|
99
|
-
return reactor.queryClient.invalidateQueries({ queryKey })
|
|
100
|
-
})
|
|
101
|
-
)
|
|
116
|
+
await invalidateAll(reactor.queryClient, factoryInvalidateQueries)
|
|
102
117
|
}
|
|
103
|
-
|
|
104
|
-
// 2. Handle hook-level invalidateQueries
|
|
118
|
+
// 2. Hook-level invalidation
|
|
105
119
|
if (hookInvalidateQueries) {
|
|
106
|
-
await
|
|
107
|
-
hookInvalidateQueries.map((queryKey) => {
|
|
108
|
-
if (queryKey) {
|
|
109
|
-
return reactor.queryClient.invalidateQueries({ queryKey })
|
|
110
|
-
}
|
|
111
|
-
return Promise.resolve()
|
|
112
|
-
})
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// 3. Call factory onSuccess
|
|
117
|
-
if (factoryOnSuccess) {
|
|
118
|
-
await factoryOnSuccess(...args)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// 4. Call hook-local onSuccess
|
|
122
|
-
if (restOptions?.onSuccess) {
|
|
123
|
-
await restOptions.onSuccess(...args)
|
|
120
|
+
await invalidateAll(reactor.queryClient, hookInvalidateQueries)
|
|
124
121
|
}
|
|
122
|
+
// 3. Factory onSuccess
|
|
123
|
+
await factoryOnSuccess?.(...args)
|
|
124
|
+
// 4. Hook onSuccess
|
|
125
|
+
await restOptions.onSuccess?.(...args)
|
|
125
126
|
},
|
|
126
127
|
onError: (error, variables, context, mutation) => {
|
|
127
|
-
// Check if this is a CanisterError (from Result { Err: E })
|
|
128
128
|
if (isCanisterError(error)) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
factoryOnCanisterError(error, variables)
|
|
132
|
-
}
|
|
133
|
-
// 2. Call hook-level onCanisterError
|
|
134
|
-
if (hookOnCanisterError) {
|
|
135
|
-
hookOnCanisterError(error, variables)
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// 3. Call factory-level onError (for all errors)
|
|
140
|
-
if (factoryOnError) {
|
|
141
|
-
factoryOnError(error, variables, context, mutation)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// 4. Call hook-level onError (for all errors)
|
|
145
|
-
if (restOptions?.onError) {
|
|
146
|
-
restOptions.onError(error, variables, context, mutation)
|
|
129
|
+
factoryOnCanisterError?.(error, variables)
|
|
130
|
+
hookOnCanisterError?.(error, variables)
|
|
147
131
|
}
|
|
132
|
+
factoryOnError?.(error, variables, context, mutation)
|
|
133
|
+
restOptions.onError?.(error, variables, context, mutation)
|
|
148
134
|
},
|
|
149
135
|
},
|
|
150
136
|
reactor.queryClient
|
|
151
137
|
)
|
|
152
138
|
}
|
|
153
139
|
|
|
154
|
-
return {
|
|
155
|
-
useMutation: useMutationHook,
|
|
156
|
-
execute,
|
|
157
|
-
}
|
|
140
|
+
return { useMutation: useMutationHook, execute }
|
|
158
141
|
}
|
|
159
142
|
|
|
160
143
|
// ============================================================================
|
|
161
|
-
// Factory Function
|
|
144
|
+
// Public Factory Function
|
|
162
145
|
// ============================================================================
|
|
163
146
|
|
|
164
147
|
export function createMutation<
|
package/src/createQuery.ts
CHANGED
|
@@ -30,6 +30,7 @@ import type {
|
|
|
30
30
|
QueryFactoryConfig,
|
|
31
31
|
NoInfer,
|
|
32
32
|
} from "./types"
|
|
33
|
+
import { buildChainedSelect } from "./utils"
|
|
33
34
|
|
|
34
35
|
// ============================================================================
|
|
35
36
|
// Internal Implementation
|
|
@@ -56,37 +57,34 @@ const createQueryImpl = <
|
|
|
56
57
|
...rest
|
|
57
58
|
} = config
|
|
58
59
|
|
|
59
|
-
const params = {
|
|
60
|
-
functionName,
|
|
61
|
-
args,
|
|
62
|
-
queryKey: customQueryKey,
|
|
63
|
-
}
|
|
60
|
+
const params = { functionName, args, queryKey: customQueryKey }
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
const getQueryKey = (): QueryKey => {
|
|
67
|
-
return reactor.generateQueryKey(params)
|
|
68
|
-
}
|
|
62
|
+
const getQueryKey = (): QueryKey => reactor.generateQueryKey(params)
|
|
69
63
|
|
|
70
|
-
//
|
|
64
|
+
// Apply config.select to raw data (shared by fetch, getCacheData, and the hook)
|
|
65
|
+
const applySelect = (raw: TData): TSelected =>
|
|
66
|
+
select ? select(raw) : (raw as unknown as TSelected)
|
|
67
|
+
|
|
68
|
+
/** Cache-first fetch for use in loaders / route preloading. */
|
|
71
69
|
const fetch = async (): Promise<TSelected> => {
|
|
72
70
|
const result = await reactor.fetchQuery(params)
|
|
73
|
-
return
|
|
71
|
+
return applySelect(result)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Fire-and-forget prefetch — warms the cache without blocking. */
|
|
75
|
+
const prefetch = (): Promise<void> => {
|
|
76
|
+
const baseOptions = reactor.getQueryOptions(params)
|
|
77
|
+
return reactor.queryClient.prefetchQuery({
|
|
78
|
+
queryKey: baseOptions.queryKey,
|
|
79
|
+
queryFn: baseOptions.queryFn,
|
|
80
|
+
staleTime,
|
|
81
|
+
})
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
// Implementation
|
|
77
84
|
const useQueryHook: UseQueryWithSelect<TData, TSelected, TError> = (
|
|
78
85
|
options: any
|
|
79
86
|
): any => {
|
|
80
87
|
const baseOptions = reactor.getQueryOptions(params)
|
|
81
|
-
|
|
82
|
-
// Chain the selects: raw -> config.select -> options.select
|
|
83
|
-
const chainedSelect = (rawData: TData) => {
|
|
84
|
-
const firstPass = config.select ? config.select(rawData) : rawData
|
|
85
|
-
if (options?.select) {
|
|
86
|
-
return options.select(firstPass)
|
|
87
|
-
}
|
|
88
|
-
return firstPass
|
|
89
|
-
}
|
|
90
88
|
return useQuery(
|
|
91
89
|
{
|
|
92
90
|
queryKey: baseOptions.queryKey,
|
|
@@ -94,50 +92,46 @@ const createQueryImpl = <
|
|
|
94
92
|
...rest,
|
|
95
93
|
...options,
|
|
96
94
|
queryFn: baseOptions.queryFn,
|
|
97
|
-
select:
|
|
95
|
+
select: buildChainedSelect(select, options?.select),
|
|
98
96
|
},
|
|
99
97
|
reactor.queryClient
|
|
100
98
|
)
|
|
101
99
|
}
|
|
102
100
|
|
|
103
|
-
// Invalidate function
|
|
104
101
|
const invalidate = async (): Promise<void> => {
|
|
105
|
-
|
|
106
|
-
await reactor.queryClient.invalidateQueries({ queryKey })
|
|
102
|
+
await reactor.queryClient.invalidateQueries({ queryKey: getQueryKey() })
|
|
107
103
|
}
|
|
108
104
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// Apply config.select to raw cache data
|
|
118
|
-
const selectedData = (
|
|
119
|
-
config.select ? config.select(cachedRawData) : cachedRawData
|
|
120
|
-
) as TData
|
|
121
|
-
|
|
122
|
-
// Apply optional select parameter
|
|
123
|
-
if (selectFn) {
|
|
124
|
-
return selectFn(selectedData)
|
|
125
|
-
}
|
|
105
|
+
const getCacheData: QueryResult<TData, TSelected, TError>["getCacheData"] = (
|
|
106
|
+
selectFn?: (data: TSelected) => unknown
|
|
107
|
+
): any => {
|
|
108
|
+
const raw = reactor.getQueryData(params)
|
|
109
|
+
if (raw === undefined) return undefined
|
|
110
|
+
const selected = applySelect(raw)
|
|
111
|
+
return selectFn ? selectFn(selected) : selected
|
|
112
|
+
}
|
|
126
113
|
|
|
127
|
-
|
|
114
|
+
const setData: QueryResult<TData, TSelected, TError>["setData"] = (
|
|
115
|
+
updater
|
|
116
|
+
) => {
|
|
117
|
+
return reactor.queryClient.setQueryData(getQueryKey(), updater as any) as
|
|
118
|
+
| TData
|
|
119
|
+
| undefined
|
|
128
120
|
}
|
|
129
121
|
|
|
130
122
|
return {
|
|
131
123
|
fetch,
|
|
124
|
+
prefetch,
|
|
132
125
|
useQuery: useQueryHook,
|
|
133
126
|
invalidate,
|
|
134
127
|
getQueryKey,
|
|
135
128
|
getCacheData,
|
|
129
|
+
setData,
|
|
136
130
|
}
|
|
137
131
|
}
|
|
138
132
|
|
|
139
133
|
// ============================================================================
|
|
140
|
-
// Factory Function
|
|
134
|
+
// Public Factory Function
|
|
141
135
|
// ============================================================================
|
|
142
136
|
|
|
143
137
|
export function createQuery<
|
|
@@ -172,26 +166,21 @@ export function createQueryFactory<
|
|
|
172
166
|
QueryResult<QueryFnData<A, M, T>, TSelected, QueryError<A, M, T>>
|
|
173
167
|
>()
|
|
174
168
|
|
|
175
|
-
return (
|
|
176
|
-
args: ReactorArgs<A, M, T>
|
|
177
|
-
): QueryResult<QueryFnData<A, M, T>, TSelected, QueryError<A, M, T>> => {
|
|
169
|
+
return (args: ReactorArgs<A, M, T>) => {
|
|
178
170
|
const key = reactor.generateQueryKey({
|
|
179
171
|
functionName: config.functionName as M,
|
|
180
172
|
args,
|
|
181
173
|
})
|
|
182
174
|
const cacheKey = JSON.stringify(key)
|
|
183
175
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
176
|
+
const existing = cache.get(cacheKey)
|
|
177
|
+
if (existing) return existing
|
|
187
178
|
|
|
188
179
|
const result = createQueryImpl<A, M, T, TSelected>(reactor, {
|
|
189
180
|
...config,
|
|
190
181
|
args,
|
|
191
182
|
})
|
|
192
|
-
|
|
193
183
|
cache.set(cacheKey, result)
|
|
194
|
-
|
|
195
184
|
return result
|
|
196
185
|
}
|
|
197
186
|
}
|
|
@@ -45,8 +45,7 @@ import {
|
|
|
45
45
|
} from "@tanstack/react-query"
|
|
46
46
|
import { CallConfig } from "@icp-sdk/core/agent"
|
|
47
47
|
import { NoInfer } from "./types"
|
|
48
|
-
|
|
49
|
-
const FACTORY_KEY_ARGS_QUERY_KEY = "__ic_reactor_factory_key_args"
|
|
48
|
+
import { mergeFactoryQueryKey } from "./utils"
|
|
50
49
|
|
|
51
50
|
type SuspenseInfiniteFactoryCallOptions = {
|
|
52
51
|
queryKey?: QueryKey
|
|
@@ -78,26 +77,6 @@ type SuspenseInfiniteQueryFactoryFn<
|
|
|
78
77
|
>
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
const mergeFactoryQueryKey = (
|
|
82
|
-
baseQueryKey?: QueryKey,
|
|
83
|
-
callQueryKey?: QueryKey,
|
|
84
|
-
keyArgs?: unknown
|
|
85
|
-
): QueryKey | undefined => {
|
|
86
|
-
const merged: unknown[] = []
|
|
87
|
-
|
|
88
|
-
if (baseQueryKey) {
|
|
89
|
-
merged.push(...baseQueryKey)
|
|
90
|
-
}
|
|
91
|
-
if (callQueryKey) {
|
|
92
|
-
merged.push(...callQueryKey)
|
|
93
|
-
}
|
|
94
|
-
if (keyArgs !== undefined) {
|
|
95
|
-
merged.push({ [FACTORY_KEY_ARGS_QUERY_KEY]: keyArgs })
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return merged.length > 0 ? merged : undefined
|
|
99
|
-
}
|
|
100
|
-
|
|
101
80
|
// ============================================================================
|
|
102
81
|
// Type Definitions
|
|
103
82
|
// ============================================================================
|
|
@@ -35,6 +35,7 @@ import type {
|
|
|
35
35
|
SuspenseQueryFactoryConfig,
|
|
36
36
|
NoInfer,
|
|
37
37
|
} from "./types"
|
|
38
|
+
import { buildChainedSelect } from "./utils"
|
|
38
39
|
|
|
39
40
|
// ============================================================================
|
|
40
41
|
// Internal Implementation
|
|
@@ -65,39 +66,35 @@ const createSuspenseQueryImpl = <
|
|
|
65
66
|
...rest
|
|
66
67
|
} = config
|
|
67
68
|
|
|
68
|
-
const params = {
|
|
69
|
-
functionName,
|
|
70
|
-
args,
|
|
71
|
-
queryKey: customQueryKey,
|
|
72
|
-
}
|
|
69
|
+
const params = { functionName, args, queryKey: customQueryKey }
|
|
73
70
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
const getQueryKey = () => reactor.generateQueryKey(params)
|
|
72
|
+
|
|
73
|
+
const applySelect = (raw: TData): TSelected =>
|
|
74
|
+
select ? select(raw) : (raw as unknown as TSelected)
|
|
78
75
|
|
|
79
|
-
|
|
76
|
+
/** Cache-first fetch for use in loaders / route preloading. */
|
|
80
77
|
const fetch = async (): Promise<TSelected> => {
|
|
81
78
|
const result = await reactor.fetchQuery(params)
|
|
82
|
-
return
|
|
79
|
+
return applySelect(result)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Fire-and-forget prefetch — warms the cache without blocking. */
|
|
83
|
+
const prefetch = (): Promise<void> => {
|
|
84
|
+
const baseOptions = reactor.getQueryOptions(params)
|
|
85
|
+
return reactor.queryClient.prefetchQuery({
|
|
86
|
+
queryKey: baseOptions.queryKey,
|
|
87
|
+
queryFn: baseOptions.queryFn,
|
|
88
|
+
staleTime,
|
|
89
|
+
})
|
|
83
90
|
}
|
|
84
91
|
|
|
85
|
-
// Implementation
|
|
86
92
|
const useSuspenseQueryHook: UseSuspenseQueryWithSelect<
|
|
87
93
|
TData,
|
|
88
94
|
TSelected,
|
|
89
95
|
TError
|
|
90
96
|
> = (options: any): any => {
|
|
91
97
|
const baseOptions = reactor.getQueryOptions(params)
|
|
92
|
-
|
|
93
|
-
// Chain the selects: raw -> config.select -> options.select
|
|
94
|
-
const chainedSelect = (rawData: TData) => {
|
|
95
|
-
const firstPass = config.select ? config.select(rawData) : rawData
|
|
96
|
-
if (options?.select) {
|
|
97
|
-
return options.select(firstPass)
|
|
98
|
-
}
|
|
99
|
-
return firstPass
|
|
100
|
-
}
|
|
101
98
|
return useSuspenseQuery(
|
|
102
99
|
{
|
|
103
100
|
queryKey: baseOptions.queryKey,
|
|
@@ -105,50 +102,48 @@ const createSuspenseQueryImpl = <
|
|
|
105
102
|
...rest,
|
|
106
103
|
...options,
|
|
107
104
|
queryFn: baseOptions.queryFn,
|
|
108
|
-
select:
|
|
105
|
+
select: buildChainedSelect(select, options?.select),
|
|
109
106
|
},
|
|
110
107
|
reactor.queryClient
|
|
111
108
|
)
|
|
112
109
|
}
|
|
113
110
|
|
|
114
|
-
// Invalidate function
|
|
115
111
|
const invalidate = async (): Promise<void> => {
|
|
116
|
-
|
|
117
|
-
await reactor.queryClient.invalidateQueries({ queryKey })
|
|
112
|
+
await reactor.queryClient.invalidateQueries({ queryKey: getQueryKey() })
|
|
118
113
|
}
|
|
119
114
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
config.select ? config.select(cachedRawData) : cachedRawData
|
|
131
|
-
) as TData
|
|
132
|
-
|
|
133
|
-
// Apply optional select parameter
|
|
134
|
-
if (selectFn) {
|
|
135
|
-
return selectFn(selectedData)
|
|
136
|
-
}
|
|
115
|
+
const getCacheData: SuspenseQueryResult<
|
|
116
|
+
TData,
|
|
117
|
+
TSelected,
|
|
118
|
+
TError
|
|
119
|
+
>["getCacheData"] = (selectFn?: (data: TSelected) => unknown): any => {
|
|
120
|
+
const raw = reactor.getQueryData(params)
|
|
121
|
+
if (raw === undefined) return undefined
|
|
122
|
+
const selected = applySelect(raw)
|
|
123
|
+
return selectFn ? selectFn(selected) : selected
|
|
124
|
+
}
|
|
137
125
|
|
|
138
|
-
|
|
126
|
+
const setData: SuspenseQueryResult<TData, TSelected, TError>["setData"] = (
|
|
127
|
+
updater
|
|
128
|
+
) => {
|
|
129
|
+
return reactor.queryClient.setQueryData(getQueryKey(), updater as any) as
|
|
130
|
+
| TData
|
|
131
|
+
| undefined
|
|
139
132
|
}
|
|
140
133
|
|
|
141
134
|
return {
|
|
142
135
|
fetch,
|
|
136
|
+
prefetch,
|
|
143
137
|
useSuspenseQuery: useSuspenseQueryHook,
|
|
144
138
|
invalidate,
|
|
145
139
|
getQueryKey,
|
|
146
140
|
getCacheData,
|
|
141
|
+
setData,
|
|
147
142
|
}
|
|
148
143
|
}
|
|
149
144
|
|
|
150
145
|
// ============================================================================
|
|
151
|
-
// Factory Function
|
|
146
|
+
// Public Factory Function
|
|
152
147
|
// ============================================================================
|
|
153
148
|
|
|
154
149
|
export function createSuspenseQuery<
|
|
@@ -186,30 +181,21 @@ export function createSuspenseQueryFactory<
|
|
|
186
181
|
SuspenseQueryResult<QueryFnData<A, M, T>, TSelected, QueryError<A, M, T>>
|
|
187
182
|
>()
|
|
188
183
|
|
|
189
|
-
return (
|
|
190
|
-
args: ReactorArgs<A, M, T>
|
|
191
|
-
): SuspenseQueryResult<
|
|
192
|
-
QueryFnData<A, M, T>,
|
|
193
|
-
TSelected,
|
|
194
|
-
QueryError<A, M, T>
|
|
195
|
-
> => {
|
|
184
|
+
return (args: ReactorArgs<A, M, T>) => {
|
|
196
185
|
const key = reactor.generateQueryKey({
|
|
197
186
|
functionName: config.functionName as M,
|
|
198
187
|
args,
|
|
199
188
|
})
|
|
200
189
|
const cacheKey = JSON.stringify(key)
|
|
201
190
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
191
|
+
const existing = cache.get(cacheKey)
|
|
192
|
+
if (existing) return existing
|
|
205
193
|
|
|
206
194
|
const result = createSuspenseQueryImpl<A, M, T, TSelected>(reactor, {
|
|
207
195
|
...(config as SuspenseQueryFactoryConfig<A, M, T, TSelected>),
|
|
208
196
|
args,
|
|
209
197
|
})
|
|
210
|
-
|
|
211
198
|
cache.set(cacheKey, result)
|
|
212
|
-
|
|
213
199
|
return result
|
|
214
200
|
}
|
|
215
201
|
}
|