@ic-reactor/react 3.0.0-beta.8 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -10
- package/dist/createActorHooks.d.ts +2 -0
- package/dist/createActorHooks.d.ts.map +1 -1
- package/dist/createActorHooks.js +2 -0
- package/dist/createActorHooks.js.map +1 -1
- package/dist/createMutation.d.ts.map +1 -1
- package/dist/createMutation.js +4 -0
- package/dist/createMutation.js.map +1 -1
- package/dist/hooks/index.d.ts +18 -5
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +15 -5
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.d.ts +13 -11
- package/dist/hooks/useActorInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.js.map +1 -1
- package/dist/hooks/useActorMethod.d.ts +105 -0
- package/dist/hooks/useActorMethod.d.ts.map +1 -0
- package/dist/hooks/useActorMethod.js +192 -0
- package/dist/hooks/useActorMethod.js.map +1 -0
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +13 -10
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +1 -1
- package/package.json +7 -6
- package/src/createActorHooks.ts +146 -0
- package/src/createAuthHooks.ts +137 -0
- package/src/createInfiniteQuery.ts +471 -0
- package/src/createMutation.ts +163 -0
- package/src/createQuery.ts +197 -0
- package/src/createSuspenseInfiniteQuery.ts +478 -0
- package/src/createSuspenseQuery.ts +215 -0
- package/src/hooks/index.ts +93 -0
- package/src/hooks/useActorInfiniteQuery.test.tsx +457 -0
- package/src/hooks/useActorInfiniteQuery.ts +134 -0
- package/src/hooks/useActorMethod.test.tsx +798 -0
- package/src/hooks/useActorMethod.ts +397 -0
- package/src/hooks/useActorMutation.test.tsx +220 -0
- package/src/hooks/useActorMutation.ts +124 -0
- package/src/hooks/useActorQuery.test.tsx +287 -0
- package/src/hooks/useActorQuery.ts +110 -0
- package/src/hooks/useActorSuspenseInfiniteQuery.test.tsx +472 -0
- package/src/hooks/useActorSuspenseInfiniteQuery.ts +137 -0
- package/src/hooks/useActorSuspenseQuery.test.tsx +254 -0
- package/src/hooks/useActorSuspenseQuery.ts +112 -0
- package/src/index.ts +21 -0
- package/src/types.ts +435 -0
- package/src/validation.ts +202 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { useMemo, useCallback } from "react"
|
|
2
|
+
import {
|
|
3
|
+
useMutation,
|
|
4
|
+
UseMutationOptions,
|
|
5
|
+
UseMutationResult,
|
|
6
|
+
QueryKey,
|
|
7
|
+
} from "@tanstack/react-query"
|
|
8
|
+
import {
|
|
9
|
+
Reactor,
|
|
10
|
+
ReactorArgs,
|
|
11
|
+
ReactorReturnOk,
|
|
12
|
+
FunctionName,
|
|
13
|
+
TransformKey,
|
|
14
|
+
ReactorReturnErr,
|
|
15
|
+
} from "@ic-reactor/core"
|
|
16
|
+
import { CallConfig } from "@icp-sdk/core/agent"
|
|
17
|
+
|
|
18
|
+
export interface UseActorMutationParameters<
|
|
19
|
+
A,
|
|
20
|
+
M extends FunctionName<A>,
|
|
21
|
+
T extends TransformKey = "candid",
|
|
22
|
+
> extends Omit<
|
|
23
|
+
UseMutationOptions<
|
|
24
|
+
ReactorReturnOk<A, M, T>,
|
|
25
|
+
ReactorReturnErr<A, M, T>,
|
|
26
|
+
ReactorArgs<A, M, T>
|
|
27
|
+
>,
|
|
28
|
+
"mutationFn"
|
|
29
|
+
> {
|
|
30
|
+
reactor: Reactor<A, T>
|
|
31
|
+
functionName: M
|
|
32
|
+
callConfig?: CallConfig
|
|
33
|
+
invalidateQueries?: QueryKey[]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type UseActorMutationConfig<
|
|
37
|
+
A,
|
|
38
|
+
M extends FunctionName<A>,
|
|
39
|
+
T extends TransformKey = "candid",
|
|
40
|
+
> = Omit<UseActorMutationParameters<A, M, T>, "reactor">
|
|
41
|
+
|
|
42
|
+
export type UseActorMutationResult<
|
|
43
|
+
A,
|
|
44
|
+
M extends FunctionName<A>,
|
|
45
|
+
T extends TransformKey = "candid",
|
|
46
|
+
> = UseMutationResult<
|
|
47
|
+
ReactorReturnOk<A, M, T>,
|
|
48
|
+
ReactorReturnErr<A, M, T>,
|
|
49
|
+
ReactorArgs<A, M, T>
|
|
50
|
+
>
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Hook for executing mutation calls on a canister.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* const { mutate, isPending } = useActorMutation({
|
|
57
|
+
* reactor,
|
|
58
|
+
* functionName: "transfer",
|
|
59
|
+
* onSuccess: () => console.log("Success!"),
|
|
60
|
+
* })
|
|
61
|
+
*/
|
|
62
|
+
export const useActorMutation = <
|
|
63
|
+
A,
|
|
64
|
+
M extends FunctionName<A>,
|
|
65
|
+
T extends TransformKey = "candid",
|
|
66
|
+
>({
|
|
67
|
+
reactor,
|
|
68
|
+
functionName,
|
|
69
|
+
invalidateQueries,
|
|
70
|
+
onSuccess,
|
|
71
|
+
callConfig,
|
|
72
|
+
...options
|
|
73
|
+
}: UseActorMutationParameters<A, M, T>): UseActorMutationResult<A, M, T> => {
|
|
74
|
+
// Memoize mutationFn to avoid creating new function on every render
|
|
75
|
+
const mutationFn = useCallback(
|
|
76
|
+
async (args: ReactorArgs<A, M, T>) => {
|
|
77
|
+
return reactor.callMethod({
|
|
78
|
+
functionName,
|
|
79
|
+
callConfig,
|
|
80
|
+
args,
|
|
81
|
+
})
|
|
82
|
+
},
|
|
83
|
+
[reactor, functionName, callConfig]
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
// Memoize onSuccess handler
|
|
87
|
+
const handleSuccess = useCallback(
|
|
88
|
+
async (
|
|
89
|
+
...params: Parameters<
|
|
90
|
+
NonNullable<
|
|
91
|
+
UseMutationOptions<
|
|
92
|
+
ReactorReturnOk<A, M, T>,
|
|
93
|
+
ReactorReturnErr<A, M, T>,
|
|
94
|
+
ReactorArgs<A, M, T>
|
|
95
|
+
>["onSuccess"]
|
|
96
|
+
>
|
|
97
|
+
>
|
|
98
|
+
) => {
|
|
99
|
+
if (invalidateQueries) {
|
|
100
|
+
await Promise.all(
|
|
101
|
+
invalidateQueries.map((queryKey) =>
|
|
102
|
+
reactor.queryClient.invalidateQueries({ queryKey })
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
if (onSuccess) {
|
|
107
|
+
await onSuccess(...params)
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
[reactor, invalidateQueries, onSuccess]
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
// Memoize mutation options
|
|
114
|
+
const mutationOptions = useMemo(
|
|
115
|
+
() => ({
|
|
116
|
+
...options,
|
|
117
|
+
mutationFn,
|
|
118
|
+
onSuccess: handleSuccess,
|
|
119
|
+
}),
|
|
120
|
+
[options, mutationFn, handleSuccess]
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return useMutation(mutationOptions, reactor.queryClient)
|
|
124
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest"
|
|
2
|
+
import { renderHook, waitFor } from "@testing-library/react"
|
|
3
|
+
import React from "react"
|
|
4
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
|
5
|
+
import { useActorQuery } from "./useActorQuery"
|
|
6
|
+
import { ActorMethod } from "@icp-sdk/core/agent"
|
|
7
|
+
import { Reactor } from "@ic-reactor/core"
|
|
8
|
+
|
|
9
|
+
// Define a test actor type
|
|
10
|
+
type TestActor = {
|
|
11
|
+
greet: ActorMethod<[], string>
|
|
12
|
+
greetWithName: ActorMethod<[string], string>
|
|
13
|
+
getCount: ActorMethod<[], bigint>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Mock Reactor
|
|
17
|
+
const createMockReactor = (queryClient: QueryClient): Reactor<TestActor> =>
|
|
18
|
+
({
|
|
19
|
+
queryClient,
|
|
20
|
+
getQueryOptions: vi.fn().mockImplementation(({ functionName, args }) => ({
|
|
21
|
+
queryKey: ["test-canister", functionName, ...(args || [])],
|
|
22
|
+
queryFn: vi.fn().mockImplementation(async () => {
|
|
23
|
+
if (functionName === "greet") return "Hello, World!"
|
|
24
|
+
if (functionName === "greetWithName")
|
|
25
|
+
return `Hello, ${args?.[0] || "Guest"}!`
|
|
26
|
+
if (functionName === "getCount") return BigInt(42)
|
|
27
|
+
return null
|
|
28
|
+
}),
|
|
29
|
+
})),
|
|
30
|
+
}) as unknown as Reactor<TestActor>
|
|
31
|
+
|
|
32
|
+
describe("useActorQuery", () => {
|
|
33
|
+
let queryClient: QueryClient
|
|
34
|
+
let mockReactor: Reactor<TestActor>
|
|
35
|
+
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
queryClient = new QueryClient({
|
|
38
|
+
defaultOptions: {
|
|
39
|
+
queries: {
|
|
40
|
+
retry: false,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
mockReactor = createMockReactor(queryClient)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
48
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
describe("basic functionality", () => {
|
|
52
|
+
it("should fetch data without select", async () => {
|
|
53
|
+
const { result } = renderHook(
|
|
54
|
+
() =>
|
|
55
|
+
useActorQuery<TestActor, "greet">({
|
|
56
|
+
reactor: mockReactor,
|
|
57
|
+
functionName: "greet",
|
|
58
|
+
}),
|
|
59
|
+
{ wrapper }
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
await waitFor(() => {
|
|
63
|
+
expect(result.current.isSuccess).toBe(true)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
expect(result.current.data).toBe("Hello, World!")
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it("should pass args to the query", async () => {
|
|
70
|
+
const { result } = renderHook(
|
|
71
|
+
() =>
|
|
72
|
+
useActorQuery<TestActor, "greetWithName">({
|
|
73
|
+
reactor: mockReactor,
|
|
74
|
+
functionName: "greetWithName",
|
|
75
|
+
args: ["Alice"],
|
|
76
|
+
}),
|
|
77
|
+
{ wrapper }
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
await waitFor(() => {
|
|
81
|
+
expect(result.current.isSuccess).toBe(true)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
expect(result.current.data).toBe("Hello, Alice!")
|
|
85
|
+
expect(mockReactor.getQueryOptions).toHaveBeenCalledWith({
|
|
86
|
+
callConfig: undefined,
|
|
87
|
+
functionName: "greetWithName",
|
|
88
|
+
args: ["Alice"],
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it("should handle bigint return types", async () => {
|
|
93
|
+
const { result } = renderHook(
|
|
94
|
+
() =>
|
|
95
|
+
useActorQuery<TestActor, "getCount">({
|
|
96
|
+
reactor: mockReactor,
|
|
97
|
+
functionName: "getCount",
|
|
98
|
+
}),
|
|
99
|
+
{ wrapper }
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
await waitFor(() => {
|
|
103
|
+
expect(result.current.isSuccess).toBe(true)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
expect(result.current.data).toBe(BigInt(42))
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
describe("select function", () => {
|
|
111
|
+
it("should transform data with select function", async () => {
|
|
112
|
+
const { result } = renderHook(
|
|
113
|
+
() =>
|
|
114
|
+
useActorQuery<TestActor, "greet", "candid", number>({
|
|
115
|
+
reactor: mockReactor,
|
|
116
|
+
functionName: "greet",
|
|
117
|
+
select: (data: string) => data.length,
|
|
118
|
+
}),
|
|
119
|
+
{ wrapper }
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
await waitFor(() => {
|
|
123
|
+
expect(result.current.isSuccess).toBe(true)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
// "Hello, World!" has 13 characters
|
|
127
|
+
expect(result.current.data).toBe(13)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it("should transform data to different type with select", async () => {
|
|
131
|
+
const { result } = renderHook(
|
|
132
|
+
() =>
|
|
133
|
+
useActorQuery<TestActor, "greet", "candid", { message: string }>({
|
|
134
|
+
reactor: mockReactor,
|
|
135
|
+
functionName: "greet",
|
|
136
|
+
select: (data: string) => ({ message: data }),
|
|
137
|
+
}),
|
|
138
|
+
{ wrapper }
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
await waitFor(() => {
|
|
142
|
+
expect(result.current.isSuccess).toBe(true)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
expect(result.current.data).toEqual({ message: "Hello, World!" })
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it("should transform bigint to number with select", async () => {
|
|
149
|
+
const { result } = renderHook(
|
|
150
|
+
() =>
|
|
151
|
+
useActorQuery<TestActor, "getCount", "candid", number>({
|
|
152
|
+
reactor: mockReactor,
|
|
153
|
+
functionName: "getCount",
|
|
154
|
+
select: (data: bigint) => Number(data),
|
|
155
|
+
}),
|
|
156
|
+
{ wrapper }
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
await waitFor(() => {
|
|
160
|
+
expect(result.current.isSuccess).toBe(true)
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
expect(result.current.data).toBe(42)
|
|
164
|
+
expect(typeof result.current.data).toBe("number")
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it("should work with array transformation in select", async () => {
|
|
168
|
+
const { result } = renderHook(
|
|
169
|
+
() =>
|
|
170
|
+
useActorQuery<TestActor, "greet", "candid", string[]>({
|
|
171
|
+
reactor: mockReactor,
|
|
172
|
+
functionName: "greet",
|
|
173
|
+
select: (data: string) => data.split(", "),
|
|
174
|
+
}),
|
|
175
|
+
{ wrapper }
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
await waitFor(() => {
|
|
179
|
+
expect(result.current.isSuccess).toBe(true)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
expect(result.current.data).toEqual(["Hello", "World!"])
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
describe("query options", () => {
|
|
187
|
+
it("should respect enabled: false option", async () => {
|
|
188
|
+
const { result } = renderHook(
|
|
189
|
+
() =>
|
|
190
|
+
useActorQuery<TestActor, "greet">({
|
|
191
|
+
reactor: mockReactor,
|
|
192
|
+
functionName: "greet",
|
|
193
|
+
enabled: false,
|
|
194
|
+
}),
|
|
195
|
+
{ wrapper }
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
// Should stay in pending state
|
|
199
|
+
expect(result.current.isPending).toBe(true)
|
|
200
|
+
expect(result.current.fetchStatus).toBe("idle")
|
|
201
|
+
expect(result.current.data).toBeUndefined()
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it("should use custom staleTime", async () => {
|
|
205
|
+
const { result } = renderHook(
|
|
206
|
+
() =>
|
|
207
|
+
useActorQuery<TestActor, "greet">({
|
|
208
|
+
reactor: mockReactor,
|
|
209
|
+
functionName: "greet",
|
|
210
|
+
staleTime: 1000 * 60 * 10, // 10 minutes
|
|
211
|
+
}),
|
|
212
|
+
{ wrapper }
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
await waitFor(() => {
|
|
216
|
+
expect(result.current.isSuccess).toBe(true)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
expect(result.current.isStale).toBe(false)
|
|
220
|
+
})
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
describe("type inference", () => {
|
|
224
|
+
it("should infer string type for greet method without select", async () => {
|
|
225
|
+
const { result } = renderHook(
|
|
226
|
+
() =>
|
|
227
|
+
useActorQuery<TestActor, "greet">({
|
|
228
|
+
reactor: mockReactor,
|
|
229
|
+
functionName: "greet",
|
|
230
|
+
}),
|
|
231
|
+
{ wrapper }
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
await waitFor(() => {
|
|
235
|
+
expect(result.current.isSuccess).toBe(true)
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
// TypeScript should infer data as string
|
|
239
|
+
const data: string | undefined = result.current.data
|
|
240
|
+
expect(typeof data).toBe("string")
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it("should infer number type when select returns number", async () => {
|
|
244
|
+
const { result } = renderHook(
|
|
245
|
+
() =>
|
|
246
|
+
useActorQuery({
|
|
247
|
+
reactor: mockReactor,
|
|
248
|
+
functionName: "greet",
|
|
249
|
+
select: (data: any) => data.length,
|
|
250
|
+
}),
|
|
251
|
+
{ wrapper }
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
await waitFor(() => {
|
|
255
|
+
expect(result.current.isSuccess).toBe(true)
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
// TypeScript should infer data as number
|
|
259
|
+
const data: number | undefined = result.current.data
|
|
260
|
+
expect(typeof data).toBe("number")
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
describe("refetching", () => {
|
|
265
|
+
it("should support manual refetch", async () => {
|
|
266
|
+
const { result } = renderHook(
|
|
267
|
+
() =>
|
|
268
|
+
useActorQuery<TestActor, "greet">({
|
|
269
|
+
reactor: mockReactor,
|
|
270
|
+
functionName: "greet",
|
|
271
|
+
}),
|
|
272
|
+
{ wrapper }
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
await waitFor(() => {
|
|
276
|
+
expect(result.current.isSuccess).toBe(true)
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
// Refetch should be available
|
|
280
|
+
expect(typeof result.current.refetch).toBe("function")
|
|
281
|
+
|
|
282
|
+
await result.current.refetch()
|
|
283
|
+
|
|
284
|
+
expect(result.current.isSuccess).toBe(true)
|
|
285
|
+
})
|
|
286
|
+
})
|
|
287
|
+
})
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useMemo } from "react"
|
|
2
|
+
import {
|
|
3
|
+
QueryKey,
|
|
4
|
+
useQuery,
|
|
5
|
+
QueryObserverOptions,
|
|
6
|
+
UseQueryResult,
|
|
7
|
+
} from "@tanstack/react-query"
|
|
8
|
+
import {
|
|
9
|
+
FunctionName,
|
|
10
|
+
Reactor,
|
|
11
|
+
TransformKey,
|
|
12
|
+
ReactorArgs,
|
|
13
|
+
ReactorReturnOk,
|
|
14
|
+
ReactorReturnErr,
|
|
15
|
+
} from "@ic-reactor/core"
|
|
16
|
+
import { CallConfig } from "@icp-sdk/core/agent"
|
|
17
|
+
|
|
18
|
+
export interface UseActorQueryParameters<
|
|
19
|
+
A,
|
|
20
|
+
M extends FunctionName<A>,
|
|
21
|
+
T extends TransformKey = "candid",
|
|
22
|
+
TSelected = ReactorReturnOk<A, M, T>,
|
|
23
|
+
> extends Omit<
|
|
24
|
+
QueryObserverOptions<
|
|
25
|
+
ReactorReturnOk<A, M, T>,
|
|
26
|
+
ReactorReturnErr<A, M, T>,
|
|
27
|
+
TSelected,
|
|
28
|
+
ReactorReturnOk<A, M, T>,
|
|
29
|
+
QueryKey
|
|
30
|
+
>,
|
|
31
|
+
"queryKey" | "queryFn"
|
|
32
|
+
> {
|
|
33
|
+
reactor: Reactor<A, T>
|
|
34
|
+
functionName: M
|
|
35
|
+
args?: ReactorArgs<A, M, T>
|
|
36
|
+
callConfig?: CallConfig
|
|
37
|
+
queryKey?: QueryKey
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type UseActorQueryConfig<
|
|
41
|
+
A,
|
|
42
|
+
M extends FunctionName<A>,
|
|
43
|
+
T extends TransformKey = "candid",
|
|
44
|
+
TSelected = ReactorReturnOk<A, M, T>,
|
|
45
|
+
> = Omit<UseActorQueryParameters<A, M, T, TSelected>, "reactor">
|
|
46
|
+
|
|
47
|
+
export type UseActorQueryResult<
|
|
48
|
+
A,
|
|
49
|
+
M extends FunctionName<A>,
|
|
50
|
+
T extends TransformKey = "candid",
|
|
51
|
+
TSelected = ReactorReturnOk<A, M, T>,
|
|
52
|
+
> = UseQueryResult<TSelected, ReactorReturnErr<A, M, T>>
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Hook for executing query calls on a canister.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* const { data, isLoading } = useActorQuery({
|
|
59
|
+
* reactor,
|
|
60
|
+
* functionName: "getUser",
|
|
61
|
+
* args: ["user-123"],
|
|
62
|
+
* })
|
|
63
|
+
*
|
|
64
|
+
* // With select transformation
|
|
65
|
+
* const { data } = useActorQuery({
|
|
66
|
+
* reactor,
|
|
67
|
+
* functionName: "getUser",
|
|
68
|
+
* args: ["user-123"],
|
|
69
|
+
* select: (user) => user.name,
|
|
70
|
+
* })
|
|
71
|
+
*/
|
|
72
|
+
export const useActorQuery = <
|
|
73
|
+
A,
|
|
74
|
+
M extends FunctionName<A>,
|
|
75
|
+
T extends TransformKey = "candid",
|
|
76
|
+
TSelected = ReactorReturnOk<A, M, T>,
|
|
77
|
+
>({
|
|
78
|
+
reactor,
|
|
79
|
+
functionName,
|
|
80
|
+
args,
|
|
81
|
+
callConfig,
|
|
82
|
+
queryKey: defaultQueryKey,
|
|
83
|
+
...options
|
|
84
|
+
}: UseActorQueryParameters<A, M, T, TSelected>): UseActorQueryResult<
|
|
85
|
+
A,
|
|
86
|
+
M,
|
|
87
|
+
T,
|
|
88
|
+
TSelected
|
|
89
|
+
> => {
|
|
90
|
+
// Memoize query options to prevent unnecessary re-computations
|
|
91
|
+
const { queryKey, queryFn } = useMemo(
|
|
92
|
+
() =>
|
|
93
|
+
reactor.getQueryOptions<M>({
|
|
94
|
+
callConfig,
|
|
95
|
+
functionName,
|
|
96
|
+
args,
|
|
97
|
+
queryKey: defaultQueryKey,
|
|
98
|
+
}),
|
|
99
|
+
[reactor, callConfig, functionName, args, defaultQueryKey]
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
return useQuery(
|
|
103
|
+
{
|
|
104
|
+
queryFn,
|
|
105
|
+
...options,
|
|
106
|
+
queryKey,
|
|
107
|
+
},
|
|
108
|
+
reactor.queryClient
|
|
109
|
+
)
|
|
110
|
+
}
|