@ic-reactor/react 3.13.0 → 4.0.0-beta.2
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 +237 -787
- package/dist/index.d.ts +260 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +471 -23
- package/dist/index.js.map +1 -1
- package/llms.txt +82 -278
- package/package.json +11 -39
- package/src/index.tsx +612 -0
- package/dist/auth/auth-client-compat.d.ts +0 -122
- package/dist/auth/auth-client-compat.d.ts.map +0 -1
- package/dist/auth/auth-client-compat.js +0 -162
- package/dist/auth/auth-client-compat.js.map +0 -1
- package/dist/auth/authentication-manager.d.ts +0 -405
- package/dist/auth/authentication-manager.d.ts.map +0 -1
- package/dist/auth/authentication-manager.js +0 -1537
- package/dist/auth/authentication-manager.js.map +0 -1
- package/dist/auth/constants.d.ts +0 -24
- package/dist/auth/constants.d.ts.map +0 -1
- package/dist/auth/constants.js +0 -24
- package/dist/auth/constants.js.map +0 -1
- package/dist/auth/createIdentityAttributeHooks.d.ts +0 -14
- package/dist/auth/createIdentityAttributeHooks.d.ts.map +0 -1
- package/dist/auth/createIdentityAttributeHooks.js +0 -122
- package/dist/auth/createIdentityAttributeHooks.js.map +0 -1
- package/dist/auth/identity-attributes-manager.d.ts +0 -27
- package/dist/auth/identity-attributes-manager.d.ts.map +0 -1
- package/dist/auth/identity-attributes-manager.js +0 -191
- package/dist/auth/identity-attributes-manager.js.map +0 -1
- package/dist/auth/identity-attributes.d.ts +0 -19
- package/dist/auth/identity-attributes.d.ts.map +0 -1
- package/dist/auth/identity-attributes.js +0 -227
- package/dist/auth/identity-attributes.js.map +0 -1
- package/dist/auth/index.d.ts +0 -8
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/auth/index.js +0 -8
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/local-ii-probe.d.ts +0 -57
- package/dist/auth/local-ii-probe.d.ts.map +0 -1
- package/dist/auth/local-ii-probe.js +0 -121
- package/dist/auth/local-ii-probe.js.map +0 -1
- package/dist/auth/types.d.ts +0 -222
- package/dist/auth/types.d.ts.map +0 -1
- package/dist/auth/types.js +0 -2
- package/dist/auth/types.js.map +0 -1
- package/dist/createActorHooks.d.ts +0 -41
- package/dist/createActorHooks.d.ts.map +0 -1
- package/dist/createActorHooks.js +0 -17
- package/dist/createActorHooks.js.map +0 -1
- package/dist/createInfiniteQuery.d.ts +0 -185
- package/dist/createInfiniteQuery.d.ts.map +0 -1
- package/dist/createInfiniteQuery.js +0 -198
- package/dist/createInfiniteQuery.js.map +0 -1
- package/dist/createMutation.d.ts +0 -33
- package/dist/createMutation.d.ts.map +0 -1
- package/dist/createMutation.js +0 -199
- package/dist/createMutation.js.map +0 -1
- package/dist/createQuery.d.ts +0 -63
- package/dist/createQuery.d.ts.map +0 -1
- package/dist/createQuery.js +0 -204
- package/dist/createQuery.js.map +0 -1
- package/dist/createReactorProvider.d.ts +0 -158
- package/dist/createReactorProvider.d.ts.map +0 -1
- package/dist/createReactorProvider.js +0 -256
- package/dist/createReactorProvider.js.map +0 -1
- package/dist/createSuspenseInfiniteQuery.d.ts +0 -154
- package/dist/createSuspenseInfiniteQuery.d.ts.map +0 -1
- package/dist/createSuspenseInfiniteQuery.js +0 -209
- package/dist/createSuspenseInfiniteQuery.js.map +0 -1
- package/dist/createSuspenseQuery.d.ts +0 -46
- package/dist/createSuspenseQuery.d.ts.map +0 -1
- package/dist/createSuspenseQuery.js +0 -158
- package/dist/createSuspenseQuery.js.map +0 -1
- package/dist/defineDisplayReactor.d.ts +0 -43
- package/dist/defineDisplayReactor.d.ts.map +0 -1
- package/dist/defineDisplayReactor.js +0 -42
- package/dist/defineDisplayReactor.js.map +0 -1
- package/dist/defineReactor.d.ts +0 -99
- package/dist/defineReactor.d.ts.map +0 -1
- package/dist/defineReactor.js +0 -15
- package/dist/defineReactor.js.map +0 -1
- package/dist/defineReactorShared.d.ts +0 -84
- package/dist/defineReactorShared.d.ts.map +0 -1
- package/dist/defineReactorShared.js +0 -139
- package/dist/defineReactorShared.js.map +0 -1
- package/dist/hooks/createAuthHooks.d.ts +0 -50
- package/dist/hooks/createAuthHooks.d.ts.map +0 -1
- package/dist/hooks/createAuthHooks.js +0 -291
- package/dist/hooks/createAuthHooks.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -21
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js +0 -24
- package/dist/hooks/index.js.map +0 -1
- package/dist/hooks/useActorInfiniteQuery.d.ts +0 -67
- package/dist/hooks/useActorInfiniteQuery.d.ts.map +0 -1
- package/dist/hooks/useActorInfiniteQuery.js +0 -89
- package/dist/hooks/useActorInfiniteQuery.js.map +0 -1
- package/dist/hooks/useActorMethod.d.ts +0 -148
- package/dist/hooks/useActorMethod.d.ts.map +0 -1
- package/dist/hooks/useActorMethod.js +0 -394
- package/dist/hooks/useActorMethod.js.map +0 -1
- package/dist/hooks/useActorMutation.d.ts +0 -51
- package/dist/hooks/useActorMutation.d.ts.map +0 -1
- package/dist/hooks/useActorMutation.js +0 -70
- package/dist/hooks/useActorMutation.js.map +0 -1
- package/dist/hooks/useActorQuery.d.ts +0 -45
- package/dist/hooks/useActorQuery.d.ts.map +0 -1
- package/dist/hooks/useActorQuery.js +0 -67
- package/dist/hooks/useActorQuery.js.map +0 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +0 -51
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +0 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.js +0 -76
- package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +0 -1
- package/dist/hooks/useActorSuspenseQuery.d.ts +0 -32
- package/dist/hooks/useActorSuspenseQuery.d.ts.map +0 -1
- package/dist/hooks/useActorSuspenseQuery.js +0 -58
- package/dist/hooks/useActorSuspenseQuery.js.map +0 -1
- package/dist/ownedAuthentication.d.ts +0 -52
- package/dist/ownedAuthentication.d.ts.map +0 -1
- package/dist/ownedAuthentication.js +0 -49
- package/dist/ownedAuthentication.js.map +0 -1
- package/dist/server.d.ts +0 -21
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -23
- package/dist/server.js.map +0 -1
- package/dist/testing.d.ts +0 -19
- package/dist/testing.d.ts.map +0 -1
- package/dist/testing.js +0 -19
- package/dist/testing.js.map +0 -1
- package/dist/types.d.ts +0 -671
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -5
- package/dist/types.js.map +0 -1
- package/dist/utils.d.ts +0 -207
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -405
- package/dist/utils.js.map +0 -1
- package/dist/validation.d.ts +0 -136
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js +0 -144
- package/dist/validation.js.map +0 -1
- package/src/auth/auth-client-compat.ts +0 -273
- package/src/auth/authentication-manager.ts +0 -1682
- package/src/auth/constants.ts +0 -32
- package/src/auth/createIdentityAttributeHooks.ts +0 -169
- package/src/auth/identity-attributes-manager.ts +0 -226
- package/src/auth/identity-attributes.ts +0 -345
- package/src/auth/index.ts +0 -7
- package/src/auth/local-ii-probe.ts +0 -173
- package/src/auth/types.ts +0 -243
- package/src/createActorHooks.ts +0 -208
- package/src/createInfiniteQuery.ts +0 -670
- package/src/createMutation.ts +0 -324
- package/src/createQuery.ts +0 -369
- package/src/createReactorProvider.ts +0 -365
- package/src/createSuspenseInfiniteQuery.ts +0 -651
- package/src/createSuspenseQuery.ts +0 -304
- package/src/defineDisplayReactor.ts +0 -62
- package/src/defineReactor.ts +0 -142
- package/src/defineReactorShared.ts +0 -268
- package/src/hooks/createAuthHooks.ts +0 -371
- package/src/hooks/index.ts +0 -103
- package/src/hooks/useActorInfiniteQuery.ts +0 -278
- package/src/hooks/useActorMethod.ts +0 -710
- package/src/hooks/useActorMutation.ts +0 -205
- package/src/hooks/useActorQuery.ts +0 -157
- package/src/hooks/useActorSuspenseInfiniteQuery.ts +0 -248
- package/src/hooks/useActorSuspenseQuery.ts +0 -147
- package/src/index.ts +0 -31
- package/src/ownedAuthentication.ts +0 -81
- package/src/server.ts +0 -23
- package/src/testing.ts +0 -18
- package/src/types.ts +0 -948
- package/src/utils.ts +0 -505
- package/src/validation.ts +0 -226
package/llms.txt
CHANGED
|
@@ -1,280 +1,84 @@
|
|
|
1
1
|
# @ic-reactor/react
|
|
2
2
|
|
|
3
|
-
> React
|
|
4
|
-
>
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
`
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
`
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
## When to Use What
|
|
86
|
-
|
|
87
|
-
| Need | Use |
|
|
88
|
-
| --------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
89
|
-
| Client-only app, raw Candid values | `defineReactor(...)` at module scope |
|
|
90
|
-
| Client-only app, values for forms and display | `defineDisplayReactor(...)` at module scope |
|
|
91
|
-
| A reactor you built yourself | `createActorHooks(reactor)` |
|
|
92
|
-
| Server-rendered app (Next.js, any SSR) | `createReactorProvider(() => defineReactor(...))` in a `"use client"` module |
|
|
93
|
-
| React Server Component, server action, route | `ClientManager` + `Reactor` from this package, built in the request; `fetchQuery` |
|
|
94
|
-
| One call used in components and loaders | `createQuery`, `createQueryFactory` (args later), `createMutation` |
|
|
95
|
-
| Suspense | `useActorSuspenseQuery`, `createSuspenseQuery`, `createSuspenseQueryFactory` |
|
|
96
|
-
| Paginated reads | `useActorInfiniteQuery`, `createInfiniteQuery`, `createInfiniteQueryFactory` |
|
|
97
|
-
| One hook for a query or update with `call()` | `useActorMethod` |
|
|
98
|
-
| Another canister with the same interface | `reactor.forCanister(canisterId)`, or `callConfig: { canisterId }` for one query |
|
|
99
|
-
| A second canister on the same sign-in | `defineReactor({ ..., authentication: firstApp.authentication })` |
|
|
100
|
-
| Sign-in with a manual setup | `createAuthHooks(new AuthenticationManager({ clientManager }))` |
|
|
101
|
-
| Tests | `installFakeReplica` + `createTestCanister` from `@ic-reactor/react/testing` |
|
|
102
|
-
|
|
103
|
-
## Inside React vs Outside React
|
|
104
|
-
|
|
105
|
-
- In components and custom hooks: `useActorQuery`, `useActorMutation`,
|
|
106
|
-
`useActorMethod`, `useAuth`, `.useQuery()`, `.useMutation()`,
|
|
107
|
-
`.useSuspenseQuery()`, `.useInfiniteQuery()`.
|
|
108
|
-
- In loaders, actions, services, scripts and non-hook tests: query objects'
|
|
109
|
-
`.fetch()`, `.prefetch()`, `.invalidate()`, `.getCacheData()`,
|
|
110
|
-
`.setData()`, `.cancel()`, `.reset()`, `.optimisticUpdate()`; mutation
|
|
111
|
-
objects' `.execute(args)`; and the reactor's `.fetchQuery()`,
|
|
112
|
-
`.getQueryData()`, `.invalidateQueries()`, `.callMethod()`.
|
|
113
|
-
- `.fetch()` and `reactor.fetchQuery()` fetch again as the new principal when a
|
|
114
|
-
sign-in or sign-out lands mid-fetch; they never reject with TanStack's
|
|
115
|
-
`CancelledError`. `.prefetch()` fetches again the same way and never
|
|
116
|
-
rejects, and a query method's `call()` / `refetch()` from `useActorMethod`
|
|
117
|
-
resolve with the new principal's answer (`undefined` if that fetch fails).
|
|
118
|
-
Wrap your own `queryClient.fetchQuery` or
|
|
119
|
-
`fetchInfiniteQuery` of a canister query in
|
|
120
|
-
`clientManager.fetchAcrossIdentitySwitch(() => ...)`.
|
|
121
|
-
|
|
122
|
-
```ts
|
|
123
|
-
import { createMutation, createQueryFactory } from "@ic-reactor/react"
|
|
124
|
-
import { backend } from "./reactor"
|
|
125
|
-
|
|
126
|
-
export const getPost = createQueryFactory(backend, { functionName: "get_post" })
|
|
127
|
-
export const likePost = createMutation(backend, {
|
|
128
|
-
functionName: "like_post",
|
|
129
|
-
invalidateQueries: [getPost], // every cached post, whatever its args
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
// In a component: getPost([id]).useQuery(), likePost.useMutation()
|
|
133
|
-
// In a loader: await getPost([id]).fetch()
|
|
134
|
-
// In an action: await likePost.execute([id])
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## Server Rendering
|
|
138
|
-
|
|
139
|
-
A reactor owns its `QueryClient` and query keys carry no caller principal, so
|
|
140
|
-
a module-scope reactor on a server is one cache shared by every request. In a
|
|
141
|
-
server-rendered app, build reactors per mounted provider:
|
|
142
|
-
|
|
143
|
-
```tsx
|
|
144
|
-
"use client"
|
|
145
|
-
import { createReactorProvider, defineReactor } from "@ic-reactor/react"
|
|
146
|
-
import { canisterId, idlFactory, type _SERVICE } from "./declarations/backend"
|
|
147
|
-
|
|
148
|
-
export const { ReactorProvider, useReactor } = createReactorProvider(() =>
|
|
149
|
-
defineReactor<_SERVICE>({ name: "backend", idlFactory, canisterId })
|
|
150
|
-
)
|
|
151
|
-
// layout: <ReactorProvider>{children}</ReactorProvider>
|
|
152
|
-
// component: const { useActorQuery, useAuth } = useReactor()
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
- The factory runs once per mounted provider (once per request on a server).
|
|
156
|
-
It may return a record (`useReactor("ledger")`) and query/mutation objects
|
|
157
|
-
built inside it. `useReactor()` is typed as the factory's return: never write
|
|
158
|
-
`as any` hook forwarders or a hand-rolled context provider.
|
|
159
|
-
- On unmount it disposes the `AuthenticationManager`s built for its value.
|
|
160
|
-
When the value holds exactly one `QueryClient` (its reactors share one
|
|
161
|
-
`ClientManager`), it renders a `QueryClientProvider` for it
|
|
162
|
-
(`{ queryClientProvider: false }` to keep your own).
|
|
163
|
-
- Props other than `children` go to the factory, read once per mount; change
|
|
164
|
-
the provider's `key` to rebuild. A provider mounted by a transition needs a
|
|
165
|
-
`<Suspense>` boundary inside it around suspending components.
|
|
166
|
-
- A React Server Component, server action or route handler may import
|
|
167
|
-
`Reactor`, `DisplayReactor`, `ClientManager` and the rest of the core runtime
|
|
168
|
-
from `@ic-reactor/react`: its `react-server` export condition resolves to an
|
|
169
|
-
entry without React. Hooks, `defineReactor`, `defineDisplayReactor`,
|
|
170
|
-
`createActorHooks`, `createReactorProvider`, the query/mutation factories,
|
|
171
|
-
`skipToken` and the auth classes are missing exports there.
|
|
172
|
-
|
|
173
|
-
## Queries
|
|
174
|
-
|
|
175
|
-
- `skipToken` in place of args waits without calling the canister:
|
|
176
|
-
`args: owner ? [account] : skipToken` in `useActorQuery`,
|
|
177
|
-
`getArgs: owner ? (page) => [...] : skipToken` in `useActorInfiniteQuery`,
|
|
178
|
-
`getBalance(owner ? [account] : skipToken).useQuery()` with
|
|
179
|
-
`createQueryFactory` (that skipped object has only `useQuery()`). The
|
|
180
|
-
suspense hooks, `createQuery` and `createSuspenseQuery` do not take it.
|
|
181
|
-
- Paginated reads: `createInfiniteQuery` (or `useActorInfiniteQuery`) takes
|
|
182
|
-
`functionName`, `initialPageParam`, `getNextPageParam` and
|
|
183
|
-
`getArgs: (page) => [...] as const`. Keep `as const`: without it
|
|
184
|
-
`createInfiniteQuery` infers an array rather than the argument tuple and
|
|
185
|
-
rejects it.
|
|
186
|
-
- A `Result` is unwrapped: `data` is the `Ok` payload and an `Err` arrives as a
|
|
187
|
-
`CanisterError` in `error`.
|
|
188
|
-
- Query hooks and objects take TanStack's options (`select`, `staleTime`,
|
|
189
|
-
`enabled`, ...) and `callConfig` (`canisterId`, `agent`,
|
|
190
|
-
`effectiveCanisterId`).
|
|
191
|
-
- Several tokens of one interface: `reactor.forCanister(canisterId)` returns a
|
|
192
|
-
memoized sibling reactor on the same `ClientManager`. Build hooks on it with
|
|
193
|
-
`useMemo(() => createActorHooks(ledger.forCanister(id)), [id])`.
|
|
194
|
-
- Types: `ReactorArgsOf<typeof reactor, "method">[0]`,
|
|
195
|
-
`ReactorDataOf<typeof reactor, "method">`,
|
|
196
|
-
`ReactorErrorOf<typeof reactor, "method">`.
|
|
197
|
-
|
|
198
|
-
## Mutations, Invalidation and Retries
|
|
199
|
-
|
|
200
|
-
- Call state-changing update methods only through `useActorMutation`,
|
|
201
|
-
`useActorMethod` or `createMutation`. A query runs its method again on every
|
|
202
|
-
refetch, and each run of an update executes on the canister.
|
|
203
|
-
- `invalidateQueries` takes query objects, query factories (every args
|
|
204
|
-
instance) and `{ functionName, args? }` methods of the mutation's reactor,
|
|
205
|
-
keyed at the canister the mutation was sent to. It is awaited before
|
|
206
|
-
`onSuccess`. Every key starts with the canister id, so a hand-written
|
|
207
|
-
`["get_posts"]` matches nothing.
|
|
208
|
-
- `onCanisterError` receives the `CanisterError` of an `Err` result
|
|
209
|
-
(`err.code`, `err.err`); `onError` receives every error.
|
|
210
|
-
- Mutations retry nothing unless `retry` is set, on them or as the
|
|
211
|
-
`QueryClient`'s `mutations.retry` default (which reaches `execute()` and
|
|
212
|
-
`useActorMethod` update calls too). Use `retry: reactorUpdateRetry`, which
|
|
213
|
-
retries only a `SysTransient` rejection, never a number.
|
|
214
|
-
- Optimistic UI: return `query.optimisticUpdate(updater)` from `onMutate`,
|
|
215
|
-
call `update?.rollback()` in `onError`, and `invalidate()` in `onSettled`.
|
|
216
|
-
When you need the `QueryClient`, use `reactor.queryClient`:
|
|
217
|
-
`useQueryClient()` throws without a `QueryClientProvider`.
|
|
218
|
-
|
|
219
|
-
## Sign-In
|
|
220
|
-
|
|
221
|
-
- `useAuth()` gives `login`, `logout`, `isAuthenticated`,
|
|
222
|
-
`isAuthenticating`, `principal`, `identity` and `error`. It reports
|
|
223
|
-
`isAuthenticating: true` until the first session restore settles, and in
|
|
224
|
-
every server render: check it before redirecting on `!isAuthenticated`.
|
|
225
|
-
- `createAuthHooks` takes an `AuthenticationManager`, never a `ClientManager`,
|
|
226
|
-
and returns `useAuth`, `useAgentState` and `useUserPrincipal`.
|
|
227
|
-
`useIdentityAttributes` comes from `createIdentityAttributeHooks`.
|
|
228
|
-
- Pass an identity-attribute nonce as a callback (`nonce: async () => ...`),
|
|
229
|
-
not an awaited value, so the popup opens within the click.
|
|
230
|
-
- `authentication.dispose()` releases the auth client the manager built,
|
|
231
|
-
without signing out.
|
|
232
|
-
|
|
233
|
-
## Testing
|
|
234
|
-
|
|
235
|
-
Run the real reactor and hooks against `installFakeReplica` and
|
|
236
|
-
`createTestCanister` from `@ic-reactor/react/testing`. Install the fake before
|
|
237
|
-
any `ClientManager` is built: for a module-scope `defineReactor`, at the top of
|
|
238
|
-
the test file, then `await import(...)` the components. Generated code is
|
|
239
|
-
module scope too: import `idlFactory` and `_SERVICE` from the canister's
|
|
240
|
-
`declarations/` folder, key the fake by the `canisterId` the generator wrote,
|
|
241
|
-
and `await import(...)` the entry after the fake. A handler returns
|
|
242
|
-
`{ Err: ... }` for a `CanisterError` and throws for a `CallError`. Reset a
|
|
243
|
-
shared reactor with `queryClient.clear()`, and test a signed-in user with
|
|
244
|
-
`clientManager.updateAgent(identity)`.
|
|
245
|
-
|
|
246
|
-
## Do Not
|
|
247
|
-
|
|
248
|
-
- Call hooks outside components and custom hooks, or create hooks with
|
|
249
|
-
`createActorHooks` / `createAuthHooks` on every render.
|
|
250
|
-
- Put a state-changing update method in `useActorQuery`, `createQuery` or
|
|
251
|
-
`createQueryFactory`.
|
|
252
|
-
- Give an update mutation a numeric `retry`; use `reactorUpdateRetry`.
|
|
253
|
-
- Hand-write query keys, or add the canister id to a `queryKey`.
|
|
254
|
-
- Write `args: [userId!]`, a placeholder account, or `as any` plus `enabled`;
|
|
255
|
-
pass `skipToken`.
|
|
256
|
-
- Check `"Ok" in data`; the `Result` is already unwrapped.
|
|
257
|
-
- Use `Number(x) / 10 ** decimals`; use `formatTokenAmount` and
|
|
258
|
-
`parseTokenAmount`.
|
|
259
|
-
- Retarget a shared reactor with `setCanisterId`; use `forCanister`.
|
|
260
|
-
- Write `defineReactor({ display: true })`; use `defineDisplayReactor`.
|
|
261
|
-
- Build reactors or managers at module scope in a server-rendered app, or
|
|
262
|
-
import hooks into a server component.
|
|
263
|
-
- Stub a reactor with `as unknown as Reactor` in tests.
|
|
264
|
-
- Edit `index.generated.ts` or `index.factories.generated.ts`.
|
|
265
|
-
|
|
266
|
-
## Docs
|
|
267
|
-
|
|
268
|
-
- React package: https://ic-reactor.b3pay.net/v3/packages/react.md
|
|
269
|
-
- React setup: https://ic-reactor.b3pay.net/v3/framework/react-setup.md
|
|
270
|
-
- Queries: https://ic-reactor.b3pay.net/v3/framework/queries.md
|
|
271
|
-
- Mutations: https://ic-reactor.b3pay.net/v3/framework/mutations.md
|
|
272
|
-
- Query caching: https://ic-reactor.b3pay.net/v3/framework/query-caching.md
|
|
273
|
-
- Query and mutation factories: https://ic-reactor.b3pay.net/v3/reference/factories/overview.md
|
|
274
|
-
- createReactorProvider: https://ic-reactor.b3pay.net/v3/reference/createReactorProvider.md
|
|
275
|
-
- Authentication: https://ic-reactor.b3pay.net/v3/guides/authentication.md
|
|
276
|
-
- Error handling: https://ic-reactor.b3pay.net/v3/guides/error-handling.md
|
|
277
|
-
- Testing: https://ic-reactor.b3pay.net/v3/guides/testing.md
|
|
278
|
-
- Index of all docs: https://ic-reactor.b3pay.net/llms.txt
|
|
279
|
-
- Full guide: https://ic-reactor.b3pay.net/llms-full.txt
|
|
280
|
-
- Agent skill for Claude Code and other agents: https://github.com/B3Pay/ic-reactor/tree/main/skill-packages/ic-reactor
|
|
3
|
+
> The React bindings of ic-reactor 4: `ReactorProvider`, `useClient` and
|
|
4
|
+
> `useAuth`. Their guide is the one `@ic-reactor/core` ships.
|
|
5
|
+
|
|
6
|
+
Applies to `@ic-reactor/react` 4.0.0-beta.2.
|
|
7
|
+
|
|
8
|
+
Read `node_modules/@ic-reactor/core/llms.txt`. It is the one guide for both
|
|
9
|
+
packages: setup, the provider and `useAuth`, reads, writes, errors, values,
|
|
10
|
+
and a complete React example, with the rules each call enforces.
|
|
11
|
+
|
|
12
|
+
This package has no hook around `useQuery` or `useMutation`: call TanStack
|
|
13
|
+
Query's own hooks with the options the client builds,
|
|
14
|
+
`useQuery(client.queryOptions(...))` and
|
|
15
|
+
`useMutation(client.mutationOptions(...))`. It never re-exports
|
|
16
|
+
`@ic-reactor/core`: import each name from the package that defines it.
|
|
17
|
+
|
|
18
|
+
## Keys follow the caller a render shows
|
|
19
|
+
|
|
20
|
+
Call `useClient()` in the body of each component that builds keys or read
|
|
21
|
+
options, on every render, and build them from what it returns there, never
|
|
22
|
+
from a client held at module scope. Do not keep what it returns past the
|
|
23
|
+
render: while a page hydrates it is a view for the anonymous caller, and a view
|
|
24
|
+
never moves on. An effect or callback that uses it closes over its render's
|
|
25
|
+
value and lists it in its dependencies (`react-hooks/exhaustive-deps`), so it
|
|
26
|
+
runs again with the client after hydrating. Kept from the hydrating render
|
|
27
|
+
anywhere else (`useState(client)`, `useRef(client)`, a `useMemo` or
|
|
28
|
+
`useCallback` with `[]`, a module variable), it stays that view: the component
|
|
29
|
+
shows the anonymous caller's data, a read it starts while the user is signed
|
|
30
|
+
in is cancelled, and a write through it still signs as the live caller.
|
|
31
|
+
Nothing warns about it. A nested `ReactorProvider` given it holds the client
|
|
32
|
+
the view was made over. `useClient()` follows the client's caller: a
|
|
33
|
+
component that calls it renders again on each sign-in, switch of account and
|
|
34
|
+
sign-out, with the new caller's keys, and never for a change of status that
|
|
35
|
+
leaves the caller as it is. It returns the provider's client object itself,
|
|
36
|
+
except while a page hydrates in a browser that holds a session: on a server and
|
|
37
|
+
while a page hydrates the caller is anonymous, as for `useAuth()`, so the
|
|
38
|
+
hydrating render gets a view of the client whose `queryKey`, `queryOptions`,
|
|
39
|
+
`caller()` and `authState()` are the anonymous caller's. It finds what the
|
|
40
|
+
server prefetched and dehydrated, matches the server's HTML and sends nothing.
|
|
41
|
+
Canisters, writes, `signIn`, `signOut` and the `QueryClient` are the client's
|
|
42
|
+
own, and a write signs as the caller current when it runs. Right after
|
|
43
|
+
hydrating, React renders the component again with the session, and each read
|
|
44
|
+
loads the user's keys once: until the user's data arrives, a read shows its
|
|
45
|
+
loading state, and a `useSuspenseQuery` read its boundary's fallback. A tab
|
|
46
|
+
whose caller is anonymous anyway (no session,
|
|
47
|
+
or one that expired or is signed in elsewhere) renders no `useClient()`
|
|
48
|
+
component again, and a client built with `identity` is always returned as it
|
|
49
|
+
is.
|
|
50
|
+
|
|
51
|
+
A signed-in reload has three accepted costs:
|
|
52
|
+
|
|
53
|
+
- A Suspense boundary still dehydrated below a component that calls
|
|
54
|
+
`useClient()` or `useAuth()` (its lazy code still loading, or its streamed
|
|
55
|
+
HTML not arrived yet) is rendered on the client when that component
|
|
56
|
+
moves on to the session: it shows its fallback, not the server's HTML, and
|
|
57
|
+
React 18 reports a recoverable error. Its data is still the user's. A
|
|
58
|
+
`useAuth()` component also moves on in a tab whose session expired or is
|
|
59
|
+
signed in elsewhere. Render such a boundary where no component that renders with the caller sits above
|
|
60
|
+
it, or pass it in as `children`, which a component's own update does not
|
|
61
|
+
render again.
|
|
62
|
+
- A read the hydrating render built may still run once (a refetch of stale
|
|
63
|
+
data on mount, or a fetch from an effect) and is then cancelled before
|
|
64
|
+
anything is sent. A key that holds data keeps it as it was, and a fetch of
|
|
65
|
+
it resolves with that data. A key with none fails, with `kind`
|
|
66
|
+
`"cancelled"` and `code` `"caller_changed"`, until the anonymous caller is
|
|
67
|
+
current again: the hydrating render's own reads never show it, but a
|
|
68
|
+
listener of `client.queryClient.getQueryCache().subscribe()` (an
|
|
69
|
+
`"updated"` event whose `action.type` is `"error"`; the client takes no
|
|
70
|
+
`QueryCache` of yours), an effect that awaits the fetch and, for a
|
|
71
|
+
`useSuspenseQuery` read the server rendered without dehydrating its data,
|
|
72
|
+
React's `onRecoverableError` (as the reported error's `cause` on React 19)
|
|
73
|
+
see it, so ignore `kind` `"cancelled"` there.
|
|
74
|
+
- Put a Suspense boundary above every component that reads with
|
|
75
|
+
`useSuspenseQuery`, on React 18 and 19 alike. The move to the session is a
|
|
76
|
+
synchronous update that such a read suspends until the user's data arrives:
|
|
77
|
+
with a boundary above, it shows the fallback, then the user's data, and the
|
|
78
|
+
rest of the page responds meanwhile. With none, React 18 refuses the update
|
|
79
|
+
("A component suspended while responding to synchronous input") and
|
|
80
|
+
unmounts the root, so a signed-in reload renders nothing. React 19 keeps the
|
|
81
|
+
server's HTML on screen, but until the user's data arrives, however long the
|
|
82
|
+
read and its retries take, a click or any other update outside a transition
|
|
83
|
+
commits nothing, and neither does a transition that renders the reading
|
|
84
|
+
component again.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ic-reactor/react",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "4.0.0-beta.2",
|
|
4
|
+
"description": "ic-reactor 4 React bindings: a provider that owns one client per tree, useClient and useAuth",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -13,15 +13,9 @@
|
|
|
13
13
|
"exports": {
|
|
14
14
|
".": {
|
|
15
15
|
"types": "./dist/index.d.ts",
|
|
16
|
-
"react-server": "./dist/server.js",
|
|
17
16
|
"import": "./dist/index.js",
|
|
18
17
|
"default": "./dist/index.js"
|
|
19
18
|
},
|
|
20
|
-
"./testing": {
|
|
21
|
-
"types": "./dist/testing.d.ts",
|
|
22
|
-
"import": "./dist/testing.js",
|
|
23
|
-
"default": "./dist/testing.js"
|
|
24
|
-
},
|
|
25
19
|
"./package.json": "./package.json"
|
|
26
20
|
},
|
|
27
21
|
"files": [
|
|
@@ -41,7 +35,7 @@
|
|
|
41
35
|
"bugs": {
|
|
42
36
|
"url": "https://github.com/B3Pay/ic-reactor/issues"
|
|
43
37
|
},
|
|
44
|
-
"homepage": "https://ic-reactor.b3pay.net/
|
|
38
|
+
"homepage": "https://ic-reactor.b3pay.net/v4/packages/react/",
|
|
45
39
|
"keywords": [
|
|
46
40
|
"internet-computer",
|
|
47
41
|
"icp",
|
|
@@ -58,62 +52,40 @@
|
|
|
58
52
|
],
|
|
59
53
|
"author": "Behrad Deylami",
|
|
60
54
|
"license": "MIT",
|
|
61
|
-
"dependencies": {
|
|
62
|
-
"@ic-reactor/core": "^3.13.0"
|
|
63
|
-
},
|
|
64
55
|
"peerDependencies": {
|
|
65
|
-
"@icp-sdk/auth": "^8.0.0 || ^10.0.0",
|
|
66
|
-
"@icp-sdk/core": "^6.1.0",
|
|
67
|
-
"@noble/curves": "^2.2.0",
|
|
68
56
|
"@tanstack/react-query": "^5.90.2",
|
|
69
57
|
"react": ">=18.0.0",
|
|
70
|
-
"
|
|
58
|
+
"@ic-reactor/core": "4.0.0-beta.2"
|
|
71
59
|
},
|
|
72
60
|
"devDependencies": {
|
|
73
|
-
"@
|
|
74
|
-
"@icp-sdk/auth-v8": "npm:@icp-sdk/auth@^8.0.3",
|
|
75
|
-
"@icp-sdk/core": "^6.1.0",
|
|
76
|
-
"@noble/curves": "^2.2.0",
|
|
61
|
+
"@candid-core/schema": "0.3.0",
|
|
77
62
|
"@size-limit/preset-small-lib": "^13.0.3",
|
|
78
63
|
"@tanstack/react-query": "^5.102.8",
|
|
79
64
|
"@testing-library/dom": "^10.4.1",
|
|
80
|
-
"@testing-library/jest-dom": "^7.0.1",
|
|
81
65
|
"@testing-library/react": "^16.3.3",
|
|
82
66
|
"@types/react": "^19.3.0",
|
|
83
67
|
"@types/react-dom": "^19.3.0",
|
|
84
|
-
"fake-indexeddb": "^6.2.5",
|
|
85
68
|
"jsdom": "^30.0.1",
|
|
86
69
|
"react": "^19.3.0",
|
|
87
70
|
"react-dom": "^19.3.0",
|
|
88
71
|
"size-limit": "^13.0.3",
|
|
89
|
-
"vitest": "^5.0.0"
|
|
72
|
+
"vitest": "^5.0.0",
|
|
73
|
+
"@ic-reactor/core": "4.0.0-beta.2"
|
|
90
74
|
},
|
|
91
75
|
"size-limit": [
|
|
92
76
|
{
|
|
93
|
-
"name": "React
|
|
77
|
+
"name": "React bindings",
|
|
94
78
|
"path": "dist/index.js",
|
|
95
|
-
"limit": "
|
|
79
|
+
"limit": "1.28 KB",
|
|
96
80
|
"gzip": true,
|
|
97
81
|
"ignore": [
|
|
98
82
|
"react",
|
|
99
|
-
"react-
|
|
83
|
+
"react/jsx-runtime",
|
|
100
84
|
"@ic-reactor/core",
|
|
101
|
-
"@tanstack/react-query"
|
|
102
|
-
"@icp-sdk/auth"
|
|
85
|
+
"@tanstack/react-query"
|
|
103
86
|
]
|
|
104
87
|
}
|
|
105
88
|
],
|
|
106
|
-
"peerDependenciesMeta": {
|
|
107
|
-
"@icp-sdk/auth": {
|
|
108
|
-
"optional": true
|
|
109
|
-
},
|
|
110
|
-
"@noble/curves": {
|
|
111
|
-
"optional": true
|
|
112
|
-
},
|
|
113
|
-
"react-dom": {
|
|
114
|
-
"optional": true
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
89
|
"scripts": {
|
|
118
90
|
"build": "rm -rf dist tsconfig.tsbuildinfo && tsc",
|
|
119
91
|
"test": "vitest run",
|