@ic-reactor/react 3.12.4 → 3.13.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/README.md +410 -44
- package/dist/auth/auth-client-compat.d.ts +122 -0
- package/dist/auth/auth-client-compat.d.ts.map +1 -0
- package/dist/auth/auth-client-compat.js +162 -0
- package/dist/auth/auth-client-compat.js.map +1 -0
- package/dist/auth/authentication-manager.d.ts +287 -5
- package/dist/auth/authentication-manager.d.ts.map +1 -1
- package/dist/auth/authentication-manager.js +920 -150
- package/dist/auth/authentication-manager.js.map +1 -1
- package/dist/auth/createIdentityAttributeHooks.d.ts.map +1 -1
- package/dist/auth/createIdentityAttributeHooks.js +36 -20
- package/dist/auth/createIdentityAttributeHooks.js.map +1 -1
- package/dist/auth/identity-attributes-manager.d.ts +2 -1
- package/dist/auth/identity-attributes-manager.d.ts.map +1 -1
- package/dist/auth/identity-attributes-manager.js +90 -6
- package/dist/auth/identity-attributes-manager.js.map +1 -1
- package/dist/auth/identity-attributes.d.ts.map +1 -1
- package/dist/auth/identity-attributes.js +57 -0
- package/dist/auth/identity-attributes.js.map +1 -1
- package/dist/auth/local-ii-probe.d.ts +12 -1
- package/dist/auth/local-ii-probe.d.ts.map +1 -1
- package/dist/auth/local-ii-probe.js +22 -3
- package/dist/auth/local-ii-probe.js.map +1 -1
- package/dist/auth/types.d.ts +48 -5
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/createActorHooks.d.ts +9 -20
- package/dist/createActorHooks.d.ts.map +1 -1
- package/dist/createActorHooks.js.map +1 -1
- package/dist/createInfiniteQuery.d.ts +51 -10
- package/dist/createInfiniteQuery.d.ts.map +1 -1
- package/dist/createInfiniteQuery.js +39 -15
- package/dist/createInfiniteQuery.js.map +1 -1
- package/dist/createMutation.d.ts +4 -1
- package/dist/createMutation.d.ts.map +1 -1
- package/dist/createMutation.js +121 -84
- package/dist/createMutation.js.map +1 -1
- package/dist/createQuery.d.ts +35 -2
- package/dist/createQuery.d.ts.map +1 -1
- package/dist/createQuery.js +104 -17
- package/dist/createQuery.js.map +1 -1
- package/dist/createReactorProvider.d.ts +158 -0
- package/dist/createReactorProvider.d.ts.map +1 -0
- package/dist/createReactorProvider.js +256 -0
- package/dist/createReactorProvider.js.map +1 -0
- package/dist/createSuspenseInfiniteQuery.d.ts +16 -9
- package/dist/createSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/createSuspenseInfiniteQuery.js +59 -27
- package/dist/createSuspenseInfiniteQuery.js.map +1 -1
- package/dist/createSuspenseQuery.d.ts +23 -2
- package/dist/createSuspenseQuery.d.ts.map +1 -1
- package/dist/createSuspenseQuery.js +68 -21
- package/dist/createSuspenseQuery.js.map +1 -1
- package/dist/defineDisplayReactor.d.ts +43 -0
- package/dist/defineDisplayReactor.d.ts.map +1 -0
- package/dist/defineDisplayReactor.js +42 -0
- package/dist/defineDisplayReactor.js.map +1 -0
- package/dist/defineReactor.d.ts +46 -72
- package/dist/defineReactor.d.ts.map +1 -1
- package/dist/defineReactor.js +11 -176
- package/dist/defineReactor.js.map +1 -1
- package/dist/defineReactorShared.d.ts +84 -0
- package/dist/defineReactorShared.d.ts.map +1 -0
- package/dist/defineReactorShared.js +139 -0
- package/dist/defineReactorShared.js.map +1 -0
- package/dist/hooks/createAuthHooks.d.ts +9 -2
- package/dist/hooks/createAuthHooks.d.ts.map +1 -1
- package/dist/hooks/createAuthHooks.js +184 -24
- package/dist/hooks/createAuthHooks.js.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.d.ts +36 -8
- package/dist/hooks/useActorInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.js +54 -21
- package/dist/hooks/useActorInfiniteQuery.js.map +1 -1
- package/dist/hooks/useActorMethod.d.ts +37 -4
- package/dist/hooks/useActorMethod.d.ts.map +1 -1
- package/dist/hooks/useActorMethod.js +201 -57
- package/dist/hooks/useActorMethod.js.map +1 -1
- package/dist/hooks/useActorMutation.d.ts +15 -12
- package/dist/hooks/useActorMutation.d.ts.map +1 -1
- package/dist/hooks/useActorMutation.js +14 -13
- package/dist/hooks/useActorMutation.js.map +1 -1
- package/dist/hooks/useActorQuery.d.ts +17 -4
- package/dist/hooks/useActorQuery.d.ts.map +1 -1
- package/dist/hooks/useActorQuery.js +30 -9
- package/dist/hooks/useActorQuery.js.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +17 -5
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.js +37 -17
- package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +1 -1
- package/dist/hooks/useActorSuspenseQuery.d.ts +2 -2
- package/dist/hooks/useActorSuspenseQuery.d.ts.map +1 -1
- package/dist/hooks/useActorSuspenseQuery.js +20 -9
- package/dist/hooks/useActorSuspenseQuery.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/ownedAuthentication.d.ts +52 -0
- package/dist/ownedAuthentication.d.ts.map +1 -0
- package/dist/ownedAuthentication.js +49 -0
- package/dist/ownedAuthentication.js.map +1 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +23 -0
- package/dist/server.js.map +1 -0
- package/dist/testing.d.ts +19 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +19 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +428 -21
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/utils.d.ts +159 -3
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +301 -1
- package/dist/utils.js.map +1 -1
- package/dist/validation.d.ts +12 -7
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +34 -15
- package/dist/validation.js.map +1 -1
- package/llms.txt +259 -33
- package/package.json +17 -5
- package/src/auth/auth-client-compat.ts +273 -0
- package/src/auth/authentication-manager.ts +918 -96
- package/src/auth/createIdentityAttributeHooks.ts +47 -21
- package/src/auth/identity-attributes-manager.ts +100 -5
- package/src/auth/identity-attributes.ts +75 -0
- package/src/auth/local-ii-probe.ts +29 -3
- package/src/auth/types.ts +49 -6
- package/src/createActorHooks.ts +50 -42
- package/src/createInfiniteQuery.ts +120 -28
- package/src/createMutation.ts +213 -132
- package/src/createQuery.ts +164 -32
- package/src/createReactorProvider.ts +365 -0
- package/src/createSuspenseInfiniteQuery.ts +93 -43
- package/src/createSuspenseQuery.ts +102 -32
- package/src/defineDisplayReactor.ts +62 -0
- package/src/defineReactor.ts +81 -263
- package/src/defineReactorShared.ts +268 -0
- package/src/hooks/createAuthHooks.ts +210 -28
- package/src/hooks/useActorInfiniteQuery.ts +156 -55
- package/src/hooks/useActorMethod.ts +295 -92
- package/src/hooks/useActorMutation.ts +42 -30
- package/src/hooks/useActorQuery.ts +43 -10
- package/src/hooks/useActorSuspenseInfiniteQuery.ts +110 -54
- package/src/hooks/useActorSuspenseQuery.ts +30 -15
- package/src/index.ts +8 -0
- package/src/ownedAuthentication.ts +81 -0
- package/src/server.ts +23 -0
- package/src/testing.ts +18 -0
- package/src/types.ts +492 -22
- package/src/utils.ts +387 -3
- package/src/validation.ts +43 -19
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
useMutation,
|
|
4
4
|
UseMutationOptions,
|
|
5
5
|
UseMutationResult,
|
|
6
|
-
QueryKey,
|
|
7
6
|
} from "@tanstack/react-query"
|
|
8
7
|
import {
|
|
9
8
|
Reactor,
|
|
@@ -19,16 +18,20 @@ import {
|
|
|
19
18
|
TransformReturnRegistry,
|
|
20
19
|
} from "@ic-reactor/core"
|
|
21
20
|
import { CallConfig } from "@icp-sdk/core/agent"
|
|
21
|
+
import { invalidateTargets, useMountQueryClient } from "../utils.js"
|
|
22
|
+
import type { InvalidationTarget } from "../types.js"
|
|
22
23
|
|
|
23
24
|
export interface UseActorMutationParameters<
|
|
24
25
|
Service,
|
|
25
26
|
Method extends FunctionName<Service>,
|
|
26
27
|
Transform extends TransformKey = "candid",
|
|
28
|
+
TOnMutateResult = unknown,
|
|
27
29
|
> extends Omit<
|
|
28
30
|
UseMutationOptions<
|
|
29
31
|
ReactorReturnOk<Service, Method, Transform>,
|
|
30
32
|
ReactorReturnErr<Service, Method, Transform>,
|
|
31
|
-
ReactorArgs<Service, Method, Transform
|
|
33
|
+
ReactorArgs<Service, Method, Transform>,
|
|
34
|
+
TOnMutateResult
|
|
32
35
|
>,
|
|
33
36
|
"mutationFn"
|
|
34
37
|
> {
|
|
@@ -36,13 +39,15 @@ export interface UseActorMutationParameters<
|
|
|
36
39
|
functionName: Method
|
|
37
40
|
callConfig?: CallConfig
|
|
38
41
|
/**
|
|
39
|
-
* Queries to invalidate upon successful mutation
|
|
42
|
+
* Queries to invalidate upon successful mutation, awaited before
|
|
43
|
+
* `onSuccess` runs.
|
|
40
44
|
*
|
|
41
|
-
*
|
|
42
|
-
* `
|
|
43
|
-
*
|
|
45
|
+
* Each entry is a query key, a query object or query factory, or a
|
|
46
|
+
* `{ functionName, args? }` method of `reactor`; see
|
|
47
|
+
* {@link InvalidationTarget}. `undefined` entries are skipped, so
|
|
48
|
+
* `[maybeQuery]` is safe when the optional query object is absent.
|
|
44
49
|
*/
|
|
45
|
-
invalidateQueries?:
|
|
50
|
+
invalidateQueries?: InvalidationTarget<Service, Transform>[]
|
|
46
51
|
/**
|
|
47
52
|
* Callback for canister-level business logic errors.
|
|
48
53
|
* Called when the canister returns a Result { Err: E } variant.
|
|
@@ -51,7 +56,8 @@ export interface UseActorMutationParameters<
|
|
|
51
56
|
onCanisterError?: (
|
|
52
57
|
error: CanisterError<
|
|
53
58
|
TransformReturnRegistry<
|
|
54
|
-
ErrResult<ActorMethodReturnType<Service[Method]
|
|
59
|
+
ErrResult<ActorMethodReturnType<Service[Method]>>,
|
|
60
|
+
Service
|
|
55
61
|
>[Transform]
|
|
56
62
|
>,
|
|
57
63
|
variables: ReactorArgs<Service, Method, Transform>
|
|
@@ -62,16 +68,22 @@ export type UseActorMutationConfig<
|
|
|
62
68
|
Service,
|
|
63
69
|
Method extends FunctionName<Service>,
|
|
64
70
|
Transform extends TransformKey = "candid",
|
|
65
|
-
|
|
71
|
+
TOnMutateResult = unknown,
|
|
72
|
+
> = Omit<
|
|
73
|
+
UseActorMutationParameters<Service, Method, Transform, TOnMutateResult>,
|
|
74
|
+
"reactor"
|
|
75
|
+
>
|
|
66
76
|
|
|
67
77
|
export type UseActorMutationResult<
|
|
68
78
|
Service,
|
|
69
79
|
Method extends FunctionName<Service>,
|
|
70
80
|
Transform extends TransformKey = "candid",
|
|
81
|
+
TOnMutateResult = unknown,
|
|
71
82
|
> = UseMutationResult<
|
|
72
83
|
ReactorReturnOk<Service, Method, Transform>,
|
|
73
84
|
ReactorReturnErr<Service, Method, Transform>,
|
|
74
|
-
ReactorArgs<Service, Method, Transform
|
|
85
|
+
ReactorArgs<Service, Method, Transform>,
|
|
86
|
+
TOnMutateResult
|
|
75
87
|
>
|
|
76
88
|
|
|
77
89
|
/**
|
|
@@ -80,7 +92,7 @@ export type UseActorMutationResult<
|
|
|
80
92
|
* For non-React usage, prefer `createMutation(...).execute(args)`.
|
|
81
93
|
*
|
|
82
94
|
* @example
|
|
83
|
-
* const { mutate, isPending } =
|
|
95
|
+
* const { mutate, isPending } = useReactorMutation({
|
|
84
96
|
* reactor,
|
|
85
97
|
* functionName: "transfer",
|
|
86
98
|
* onSuccess: () => console.log("Success!"),
|
|
@@ -100,6 +112,7 @@ export const useActorMutation = <
|
|
|
100
112
|
Service,
|
|
101
113
|
Method extends FunctionName<Service>,
|
|
102
114
|
Transform extends TransformKey = "candid",
|
|
115
|
+
TOnMutateResult = unknown,
|
|
103
116
|
>({
|
|
104
117
|
reactor,
|
|
105
118
|
functionName,
|
|
@@ -112,8 +125,11 @@ export const useActorMutation = <
|
|
|
112
125
|
}: UseActorMutationParameters<
|
|
113
126
|
Service,
|
|
114
127
|
Method,
|
|
115
|
-
Transform
|
|
116
|
-
|
|
128
|
+
Transform,
|
|
129
|
+
TOnMutateResult
|
|
130
|
+
>): UseActorMutationResult<Service, Method, Transform, TOnMutateResult> => {
|
|
131
|
+
useMountQueryClient(reactor.queryClient)
|
|
132
|
+
|
|
117
133
|
const mutationFn = useCallback(
|
|
118
134
|
async (args: ReactorArgs<Service, Method, Transform>) =>
|
|
119
135
|
reactor.callMethod({ functionName, callConfig, args }),
|
|
@@ -127,29 +143,19 @@ export const useActorMutation = <
|
|
|
127
143
|
UseMutationOptions<
|
|
128
144
|
ReactorReturnOk<Service, Method, Transform>,
|
|
129
145
|
ReactorReturnErr<Service, Method, Transform>,
|
|
130
|
-
ReactorArgs<Service, Method, Transform
|
|
146
|
+
ReactorArgs<Service, Method, Transform>,
|
|
147
|
+
TOnMutateResult
|
|
131
148
|
>["onSuccess"]
|
|
132
149
|
>
|
|
133
150
|
>
|
|
134
151
|
) => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// whenever the optional query object is absent — would invalidate every
|
|
140
|
-
// query in the client, including an app's unrelated non-canister ones.
|
|
141
|
-
// `createMutation.invalidateAll` already filters the same way.
|
|
142
|
-
await Promise.all(
|
|
143
|
-
invalidateQueries
|
|
144
|
-
.filter((queryKey) => queryKey !== undefined)
|
|
145
|
-
.map((queryKey) =>
|
|
146
|
-
reactor.queryClient.invalidateQueries({ queryKey })
|
|
147
|
-
)
|
|
148
|
-
)
|
|
149
|
-
}
|
|
152
|
+
// Undefined entries are skipped: React Query reads
|
|
153
|
+
// `{ queryKey: undefined }` as "match everything", and the natural
|
|
154
|
+
// `[maybeQuery]` idiom produces one whenever the query is absent.
|
|
155
|
+
await invalidateTargets(reactor, invalidateQueries, callConfig)
|
|
150
156
|
await onSuccess?.(...params)
|
|
151
157
|
},
|
|
152
|
-
[reactor, invalidateQueries, onSuccess]
|
|
158
|
+
[reactor, invalidateQueries, onSuccess, callConfig]
|
|
153
159
|
)
|
|
154
160
|
|
|
155
161
|
const handleError = useCallback(
|
|
@@ -183,6 +189,12 @@ export const useActorMutation = <
|
|
|
183
189
|
// rather than the reference.
|
|
184
190
|
return useMutation(
|
|
185
191
|
{
|
|
192
|
+
// Keyed like `createMutation`'s hook and `useActorMethod`'s mutations,
|
|
193
|
+
// so `useIsMutating({ mutationKey })`, `useMutationState` and defaults
|
|
194
|
+
// registered with `setMutationDefaults` can find this one too. An
|
|
195
|
+
// explicit `mutationKey` in the options still wins.
|
|
196
|
+
mutationKey: reactor.getQueryOptions({ functionName, callConfig })
|
|
197
|
+
.queryKey,
|
|
186
198
|
...options,
|
|
187
199
|
mutationFn,
|
|
188
200
|
onSuccess: handleSuccess,
|
|
@@ -2,8 +2,10 @@ import { useMemo } from "react"
|
|
|
2
2
|
import {
|
|
3
3
|
QueryKey,
|
|
4
4
|
useQuery,
|
|
5
|
+
skipToken,
|
|
5
6
|
QueryObserverOptions,
|
|
6
7
|
UseQueryResult,
|
|
8
|
+
type SkipToken,
|
|
7
9
|
} from "@tanstack/react-query"
|
|
8
10
|
import {
|
|
9
11
|
FunctionName,
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
ReactorReturnErr,
|
|
16
18
|
} from "@ic-reactor/core"
|
|
17
19
|
import { CallConfig } from "@icp-sdk/core/agent"
|
|
20
|
+
import { retryOption, skippedQueryKey, useMountQueryClient } from "../utils.js"
|
|
18
21
|
|
|
19
22
|
export interface UseActorQueryParameters<
|
|
20
23
|
Service,
|
|
@@ -33,7 +36,13 @@ export interface UseActorQueryParameters<
|
|
|
33
36
|
> {
|
|
34
37
|
reactor: Reactor<Service, Transform>
|
|
35
38
|
functionName: Method
|
|
36
|
-
|
|
39
|
+
/**
|
|
40
|
+
* The method's arguments, or TanStack Query's `skipToken` while they are
|
|
41
|
+
* not known: the query then waits without fetching, in an entry of its own
|
|
42
|
+
* under its method's key, which no call's key shares, so it shows no data
|
|
43
|
+
* until the arguments arrive.
|
|
44
|
+
*/
|
|
45
|
+
args?: ReactorArgs<Service, Method, Transform> | SkipToken
|
|
37
46
|
callConfig?: CallConfig
|
|
38
47
|
queryKey?: QueryKey
|
|
39
48
|
}
|
|
@@ -59,19 +68,26 @@ export type UseActorQueryResult<
|
|
|
59
68
|
* Hook for executing query calls on a canister.
|
|
60
69
|
*
|
|
61
70
|
* @example
|
|
62
|
-
* const { data, isLoading } =
|
|
71
|
+
* const { data, isLoading } = useReactorQuery({
|
|
63
72
|
* reactor,
|
|
64
73
|
* functionName: "getUser",
|
|
65
74
|
* args: ["user-123"],
|
|
66
75
|
* })
|
|
67
76
|
*
|
|
68
77
|
* // With select transformation
|
|
69
|
-
* const { data } =
|
|
78
|
+
* const { data } = useReactorQuery({
|
|
70
79
|
* reactor,
|
|
71
80
|
* functionName: "getUser",
|
|
72
81
|
* args: ["user-123"],
|
|
73
82
|
* select: (user) => user.name,
|
|
74
83
|
* })
|
|
84
|
+
*
|
|
85
|
+
* // Wait for the args: no fetch until userId is known
|
|
86
|
+
* const { data } = useReactorQuery({
|
|
87
|
+
* reactor,
|
|
88
|
+
* functionName: "getUser",
|
|
89
|
+
* args: userId ? [userId] : skipToken,
|
|
90
|
+
* })
|
|
75
91
|
*/
|
|
76
92
|
export const useActorQuery = <
|
|
77
93
|
Service,
|
|
@@ -91,15 +107,31 @@ export const useActorQuery = <
|
|
|
91
107
|
Transform,
|
|
92
108
|
Selected
|
|
93
109
|
>): UseActorQueryResult<Service, Method, Transform, Selected> => {
|
|
110
|
+
useMountQueryClient(reactor.queryClient)
|
|
111
|
+
|
|
94
112
|
// Memoize query options to prevent unnecessary re-computations
|
|
95
|
-
|
|
113
|
+
// For an update method, the options also carry its default `retry`; see
|
|
114
|
+
// `Reactor.getQueryRetry`.
|
|
115
|
+
const { queryKey, queryFn, retry } = useMemo(
|
|
96
116
|
() =>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
117
|
+
args === skipToken
|
|
118
|
+
? // Waiting for its args: an entry of its own under the method's key,
|
|
119
|
+
// with nothing to run until then; see skippedQueryKey.
|
|
120
|
+
{
|
|
121
|
+
queryKey: skippedQueryKey(
|
|
122
|
+
reactor.generateQueryKey({ functionName }, callConfig),
|
|
123
|
+
"query"
|
|
124
|
+
),
|
|
125
|
+
// Kept as the unique symbol, which an object literal widens.
|
|
126
|
+
queryFn: skipToken as SkipToken,
|
|
127
|
+
retry: undefined,
|
|
128
|
+
}
|
|
129
|
+
: reactor.getQueryOptions<Method>({
|
|
130
|
+
callConfig,
|
|
131
|
+
functionName,
|
|
132
|
+
args,
|
|
133
|
+
queryKey: defaultQueryKey,
|
|
134
|
+
}),
|
|
103
135
|
// `canisterId` is mutable reactor state that `setCanisterId` can change,
|
|
104
136
|
// while `reactor` itself stays the same object — so it has to be a
|
|
105
137
|
// dependency in its own right or the key stays pinned to the old canister.
|
|
@@ -118,6 +150,7 @@ export const useActorQuery = <
|
|
|
118
150
|
queryFn,
|
|
119
151
|
...options,
|
|
120
152
|
queryKey,
|
|
153
|
+
...retryOption(options.retry, retry),
|
|
121
154
|
},
|
|
122
155
|
reactor.queryClient
|
|
123
156
|
)
|
|
@@ -16,7 +16,14 @@ import {
|
|
|
16
16
|
ReactorReturnErr,
|
|
17
17
|
} from "@ic-reactor/core"
|
|
18
18
|
import { CallConfig } from "@icp-sdk/core/agent"
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
callConfigForKey,
|
|
21
|
+
mergeFactoryQueryKey,
|
|
22
|
+
mountWhileSuspended,
|
|
23
|
+
normalizeQueryData,
|
|
24
|
+
retryOption,
|
|
25
|
+
useMountQueryClient,
|
|
26
|
+
} from "../utils.js"
|
|
20
27
|
|
|
21
28
|
/**
|
|
22
29
|
* Parameters for useActorSuspenseInfiniteQuery hook.
|
|
@@ -47,6 +54,18 @@ export interface UseActorSuspenseInfiniteQueryParameters<
|
|
|
47
54
|
functionName: Method
|
|
48
55
|
/** Function to get args from page parameter */
|
|
49
56
|
getArgs: (pageParam: TPageParam) => ReactorArgs<Service, Method, Transform>
|
|
57
|
+
/**
|
|
58
|
+
* Narrows what the cache key derives from the call arguments.
|
|
59
|
+
*
|
|
60
|
+
* By default the key is scoped by `getArgs(initialPageParam)`, so two
|
|
61
|
+
* infinite queries on the same method with different arguments stay in
|
|
62
|
+
* separate cache entries. Supply this when those args embed the cursor and
|
|
63
|
+
* only part of them identifies the query — return the stable, serializable
|
|
64
|
+
* portion (typically everything except the pagination field). It is required
|
|
65
|
+
* when `initialPageParam` changes between renders, as `Date.now()` does:
|
|
66
|
+
* without it every render keys a new query.
|
|
67
|
+
*/
|
|
68
|
+
getKeyArgs?: (args: ReactorArgs<Service, Method, Transform>) => unknown
|
|
50
69
|
/** Agent call configuration (effectiveCanisterId, etc.) */
|
|
51
70
|
callConfig?: CallConfig
|
|
52
71
|
/** Custom query key (auto-generated if not provided) */
|
|
@@ -67,12 +86,17 @@ export type UseActorSuspenseInfiniteQueryConfig<
|
|
|
67
86
|
Method extends FunctionName<Service>,
|
|
68
87
|
Transform extends TransformKey = "candid",
|
|
69
88
|
TPageParam = unknown,
|
|
89
|
+
Selected = InfiniteData<
|
|
90
|
+
ReactorQueryData<ReactorReturnOk<Service, Method, Transform>>,
|
|
91
|
+
TPageParam
|
|
92
|
+
>,
|
|
70
93
|
> = Omit<
|
|
71
94
|
UseActorSuspenseInfiniteQueryParameters<
|
|
72
95
|
Service,
|
|
73
96
|
Method,
|
|
74
97
|
Transform,
|
|
75
|
-
TPageParam
|
|
98
|
+
TPageParam,
|
|
99
|
+
Selected
|
|
76
100
|
>,
|
|
77
101
|
"reactor"
|
|
78
102
|
>
|
|
@@ -82,11 +106,12 @@ export type UseActorSuspenseInfiniteQueryResult<
|
|
|
82
106
|
Method extends FunctionName<Service>,
|
|
83
107
|
Transform extends TransformKey = "candid",
|
|
84
108
|
TPageParam = unknown,
|
|
85
|
-
|
|
86
|
-
InfiniteData<
|
|
109
|
+
Selected = InfiniteData<
|
|
87
110
|
ReactorQueryData<ReactorReturnOk<Service, Method, Transform>>,
|
|
88
111
|
TPageParam
|
|
89
112
|
>,
|
|
113
|
+
> = UseSuspenseInfiniteQueryResult<
|
|
114
|
+
Selected,
|
|
90
115
|
ReactorReturnErr<Service, Method, Transform>
|
|
91
116
|
>
|
|
92
117
|
|
|
@@ -94,10 +119,10 @@ export type UseActorSuspenseInfiniteQueryResult<
|
|
|
94
119
|
* Hook for executing suspense-enabled infinite/paginated query calls on a canister.
|
|
95
120
|
*
|
|
96
121
|
* @example
|
|
97
|
-
* const { data, fetchNextPage, hasNextPage } =
|
|
122
|
+
* const { data, fetchNextPage, hasNextPage } = useReactorSuspenseInfiniteQuery({
|
|
98
123
|
* reactor,
|
|
99
124
|
* functionName: "getItems",
|
|
100
|
-
* getArgs: (pageParam) => [{ offset: pageParam, limit: 10 }],
|
|
125
|
+
* getArgs: (pageParam) => [{ offset: pageParam, limit: 10 }] as const,
|
|
101
126
|
* initialPageParam: 0,
|
|
102
127
|
* getNextPageParam: (lastPage) => lastPage.nextOffset,
|
|
103
128
|
* })
|
|
@@ -107,10 +132,15 @@ export const useActorSuspenseInfiniteQuery = <
|
|
|
107
132
|
Method extends FunctionName<Service>,
|
|
108
133
|
Transform extends TransformKey = "candid",
|
|
109
134
|
TPageParam = unknown,
|
|
135
|
+
Selected = InfiniteData<
|
|
136
|
+
ReactorQueryData<ReactorReturnOk<Service, Method, Transform>>,
|
|
137
|
+
TPageParam
|
|
138
|
+
>,
|
|
110
139
|
>({
|
|
111
140
|
reactor,
|
|
112
141
|
functionName,
|
|
113
142
|
getArgs,
|
|
143
|
+
getKeyArgs,
|
|
114
144
|
callConfig,
|
|
115
145
|
queryKey,
|
|
116
146
|
...options
|
|
@@ -118,56 +148,68 @@ export const useActorSuspenseInfiniteQuery = <
|
|
|
118
148
|
Service,
|
|
119
149
|
Method,
|
|
120
150
|
Transform,
|
|
121
|
-
TPageParam
|
|
151
|
+
TPageParam,
|
|
152
|
+
Selected
|
|
122
153
|
>): UseActorSuspenseInfiniteQueryResult<
|
|
123
154
|
Service,
|
|
124
155
|
Method,
|
|
125
156
|
Transform,
|
|
126
|
-
TPageParam
|
|
157
|
+
TPageParam,
|
|
158
|
+
Selected
|
|
127
159
|
> => {
|
|
160
|
+
useMountQueryClient(reactor.queryClient)
|
|
161
|
+
|
|
128
162
|
// Always pass queryKey through generateQueryKey so it is merged with the
|
|
129
163
|
// reactor/function identity. Using the custom key verbatim would cause cache
|
|
130
164
|
// collisions if two different actors or methods share the same key string.
|
|
131
|
-
const baseQueryKey = useMemo(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
165
|
+
const baseQueryKey = useMemo(() => {
|
|
166
|
+
// Fold the call arguments into the key. They live in the `getArgs`
|
|
167
|
+
// closure rather than in the config, so without this two hooks on the
|
|
168
|
+
// same method with different arguments share one cache entry and serve
|
|
169
|
+
// each other's pages. `getKeyArgs` narrows them exactly as it does for the
|
|
170
|
+
// factories, which the bound hook's config type always accepted: it used
|
|
171
|
+
// to be ignored here, so the cursor stayed in the key, and an
|
|
172
|
+
// `initialPageParam` that changes every render (`Date.now()`) keyed a new
|
|
173
|
+
// query on every render — an endless loop of first-page fetches.
|
|
174
|
+
const initialArgs = getArgs(options.initialPageParam)
|
|
175
|
+
const keyArgs = getKeyArgs?.(initialArgs) ?? initialArgs
|
|
176
|
+
|
|
177
|
+
return reactor.generateQueryKey(
|
|
178
|
+
{
|
|
179
|
+
functionName,
|
|
180
|
+
queryKey: mergeFactoryQueryKey(queryKey, undefined, keyArgs),
|
|
181
|
+
},
|
|
182
|
+
callConfig
|
|
183
|
+
)
|
|
184
|
+
}, [
|
|
185
|
+
queryKey,
|
|
186
|
+
reactor,
|
|
187
|
+
// `canisterId` is mutable reactor state that `setCanisterId` can change,
|
|
188
|
+
// while `reactor` itself stays the same object — so it has to be a
|
|
189
|
+
// dependency in its own right or the key stays pinned to the old canister
|
|
190
|
+
// while the queryFn already calls the new one.
|
|
191
|
+
reactor.canisterId?.toString(),
|
|
192
|
+
functionName,
|
|
193
|
+
callConfig,
|
|
194
|
+
getArgs,
|
|
195
|
+
getKeyArgs,
|
|
196
|
+
options.initialPageParam,
|
|
197
|
+
])
|
|
162
198
|
|
|
163
199
|
// Memoize queryFn to prevent recreation on every render
|
|
164
200
|
const queryFn = useCallback(
|
|
165
|
-
async ({
|
|
201
|
+
async ({
|
|
202
|
+
pageParam,
|
|
203
|
+
queryKey: fetchedKey,
|
|
204
|
+
}: {
|
|
205
|
+
pageParam: TPageParam
|
|
206
|
+
queryKey: QueryKey
|
|
207
|
+
}) => {
|
|
166
208
|
const args = getArgs(pageParam)
|
|
167
209
|
const result = await reactor.callMethod({
|
|
168
210
|
functionName,
|
|
169
211
|
args,
|
|
170
|
-
callConfig,
|
|
212
|
+
callConfig: callConfigForKey(fetchedKey, callConfig),
|
|
171
213
|
})
|
|
172
214
|
return normalizeQueryData<ReactorReturnOk<Service, Method, Transform>>(
|
|
173
215
|
result as ReactorReturnOk<Service, Method, Transform>
|
|
@@ -176,17 +218,31 @@ export const useActorSuspenseInfiniteQuery = <
|
|
|
176
218
|
[reactor, functionName, getArgs, callConfig]
|
|
177
219
|
)
|
|
178
220
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
221
|
+
// The method's default `retry`: for an update method, only failures that
|
|
222
|
+
// prove the canister never ran the call; see `Reactor.getQueryRetry`.
|
|
223
|
+
const defaultRetry = useMemo(
|
|
224
|
+
() => reactor.getQueryRetry(functionName, baseQueryKey),
|
|
225
|
+
[reactor, functionName, baseQueryKey]
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
return useSuspenseInfiniteQuery(
|
|
230
|
+
{
|
|
231
|
+
queryKey: baseQueryKey,
|
|
232
|
+
queryFn,
|
|
233
|
+
...options,
|
|
234
|
+
...retryOption(options.retry, defaultRetry),
|
|
235
|
+
} as any,
|
|
236
|
+
reactor.queryClient
|
|
237
|
+
) as UseActorSuspenseInfiniteQueryResult<
|
|
238
|
+
Service,
|
|
239
|
+
Method,
|
|
240
|
+
Transform,
|
|
241
|
+
TPageParam,
|
|
242
|
+
Selected
|
|
243
|
+
>
|
|
244
|
+
} catch (thrown) {
|
|
245
|
+
mountWhileSuspended(reactor.queryClient, thrown)
|
|
246
|
+
throw thrown
|
|
247
|
+
}
|
|
192
248
|
}
|
|
@@ -16,6 +16,11 @@ import {
|
|
|
16
16
|
ReactorReturnErr,
|
|
17
17
|
} from "@ic-reactor/core"
|
|
18
18
|
import { CallConfig } from "@icp-sdk/core/agent"
|
|
19
|
+
import {
|
|
20
|
+
mountWhileSuspended,
|
|
21
|
+
retryOption,
|
|
22
|
+
useMountQueryClient,
|
|
23
|
+
} from "../utils.js"
|
|
19
24
|
|
|
20
25
|
export interface UseActorSuspenseQueryParameters<
|
|
21
26
|
Service,
|
|
@@ -63,14 +68,14 @@ export type UseActorSuspenseQueryResult<
|
|
|
63
68
|
* Hook for executing suspense-enabled query calls on a canister.
|
|
64
69
|
*
|
|
65
70
|
* @example
|
|
66
|
-
* const { data } =
|
|
71
|
+
* const { data } = useReactorSuspenseQuery({
|
|
67
72
|
* reactor,
|
|
68
73
|
* functionName: "getUser",
|
|
69
74
|
* args: ["user-123"],
|
|
70
75
|
* })
|
|
71
76
|
*
|
|
72
77
|
* // With select transformation
|
|
73
|
-
* const { data } =
|
|
78
|
+
* const { data } = useReactorSuspenseQuery({
|
|
74
79
|
* reactor,
|
|
75
80
|
* functionName: "getUser",
|
|
76
81
|
* args: ["user-123"],
|
|
@@ -95,8 +100,12 @@ export const useActorSuspenseQuery = <
|
|
|
95
100
|
Transform,
|
|
96
101
|
Selected
|
|
97
102
|
>): UseActorSuspenseQueryResult<Service, Method, Transform, Selected> => {
|
|
103
|
+
useMountQueryClient(reactor.queryClient)
|
|
104
|
+
|
|
98
105
|
// Memoize query options to prevent unnecessary re-computations
|
|
99
|
-
|
|
106
|
+
// For an update method, the options also carry its default `retry`; see
|
|
107
|
+
// `Reactor.getQueryRetry`.
|
|
108
|
+
const { queryKey, queryFn, retry } = useMemo(
|
|
100
109
|
() =>
|
|
101
110
|
reactor.getQueryOptions<Method>({
|
|
102
111
|
callConfig,
|
|
@@ -117,16 +126,22 @@ export const useActorSuspenseQuery = <
|
|
|
117
126
|
]
|
|
118
127
|
)
|
|
119
128
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
try {
|
|
130
|
+
return useSuspenseQuery(
|
|
131
|
+
{
|
|
132
|
+
// Suspense queries don't support skipToken, so cast the queryFn
|
|
133
|
+
queryFn: queryFn as QueryFunction<
|
|
134
|
+
ReactorQueryData<ReactorReturnOk<Service, Method, Transform>>,
|
|
135
|
+
QueryKey
|
|
136
|
+
>,
|
|
137
|
+
...options,
|
|
138
|
+
queryKey,
|
|
139
|
+
...retryOption(options.retry, retry),
|
|
140
|
+
},
|
|
141
|
+
reactor.queryClient
|
|
142
|
+
)
|
|
143
|
+
} catch (thrown) {
|
|
144
|
+
mountWhileSuspended(reactor.queryClient, thrown)
|
|
145
|
+
throw thrown
|
|
146
|
+
}
|
|
132
147
|
}
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,8 @@ export * from "./validation.js"
|
|
|
11
11
|
// React-specific exports
|
|
12
12
|
export * from "./createActorHooks.js"
|
|
13
13
|
export * from "./defineReactor.js"
|
|
14
|
+
export * from "./defineDisplayReactor.js"
|
|
15
|
+
export * from "./createReactorProvider.js"
|
|
14
16
|
|
|
15
17
|
export * from "./createQuery.js"
|
|
16
18
|
export * from "./createSuspenseQuery.js"
|
|
@@ -21,3 +23,9 @@ export * from "./createMutation.js"
|
|
|
21
23
|
export * from "./auth/index.js"
|
|
22
24
|
|
|
23
25
|
export * from "./types.js"
|
|
26
|
+
|
|
27
|
+
// TanStack Query's marker for a query whose args are not known yet, which the
|
|
28
|
+
// non-suspense query hooks and createQueryFactory take in place of args. It is
|
|
29
|
+
// the same symbol as @tanstack/react-query's, so either import works.
|
|
30
|
+
export { skipToken } from "@tanstack/react-query"
|
|
31
|
+
export type { SkipToken } from "@tanstack/react-query"
|