@ic-reactor/react 3.0.0-beta.1 → 3.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.
Files changed (2) hide show
  1. package/README.md +256 -86
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,147 +1,317 @@
1
1
  # @ic-reactor/react
2
2
 
3
- **The Ultimate React Hooks for the Internet Computer.**
4
- Connect your React application to the Internet Computer Blockchain in seconds. using the power of [TanStack Query](https://tanstack.com/query).
3
+ <div align="center">
4
+ <strong>The Ultimate React Hooks for the Internet Computer.</strong>
5
+ <br><br>
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@ic-reactor/react.svg)](https://www.npmjs.com/package/@ic-reactor/react)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
9
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
10
+ </div>
11
+
12
+ ---
13
+
14
+ Connect your React application to the Internet Computer Blockchain with full [TanStack Query](https://tanstack.com/query) integration for caching, suspense, and infinite queries.
5
15
 
6
16
  ## Features
7
17
 
8
- - ⚛️ **React Query Integration**: Full power of TanStack Query (Caching, Refetching, Suspense).
9
- - 🔄 ** Type-Safe**: Full TypeScript support with automatic type inference from your Candid files.
10
- - 🔐 **Authentication**: Easy-to-use authentication hooks with Internet Identity and other providers.
11
- - 🚀 **Performance**: Efficient caching and state management tailored for IC.
18
+ - ⚛️ **TanStack Query Integration** Full power of React Query (caching, refetching, suspense, infinite queries)
19
+ - **End-to-End Type Safety** Automatic type inference from your Candid files
20
+ - **Auto Transformations** — `DisplayReactor` converts BigInt to string, Principal to text, and more
21
+ - 📦 **Result Unwrapping** Automatic `Ok`/`Err` handling from Candid Result types
22
+ - 🔐 **Authentication** — Easy-to-use hooks with Internet Identity integration
23
+ - 🏗️ **Multi-Actor Support** — Manage multiple canisters with shared authentication
12
24
 
13
25
  ## Installation
14
26
 
15
27
  ```bash
16
- npm install @ic-reactor/react @ic-reactor/core @tanstack/react-query @icp-sdk/core @icp-sdk/auth
28
+ # With npm
29
+ npm install @ic-reactor/react @tanstack/react-query @icp-sdk/core
30
+
31
+ # With pnpm
32
+ pnpm add @ic-reactor/react @tanstack/react-query @icp-sdk/core
33
+
34
+ # Optional: For Internet Identity authentication
35
+ npm install @icp-sdk/auth
17
36
  ```
18
37
 
19
38
  ## Quick Start
20
39
 
21
- ### 1. Initialize the Actor
22
-
23
- Create your actor hooks using `createActorHooks`. This is the "One Stop Shop" for interacting with your canister.
40
+ ### 1. Setup ClientManager and Reactor
24
41
 
25
42
  ```typescript
26
- // src/hooks/actor.ts
27
- import { createActorHooks } from "@ic-reactor/react"
28
- import { canisterId, idlFactory } from "../declarations/my_canister"
29
-
30
- export const { useActorQuery, useActorMutation, useAuth, reactor } =
31
- createActorHooks({
32
- canisterId,
33
- idlFactory,
34
- })
43
+ // src/reactor.ts
44
+ import { ClientManager, Reactor } from "@ic-reactor/react"
45
+ import { QueryClient } from "@tanstack/react-query"
46
+ import { idlFactory, type _SERVICE } from "./declarations/my_canister"
47
+
48
+ // Create query client for caching
49
+ export const queryClient = new QueryClient()
50
+
51
+ // Create client manager (handles identity and agent)
52
+ export const clientManager = new ClientManager({
53
+ queryClient,
54
+ withProcessEnv: true, // Reads DFX_NETWORK from environment
55
+ })
56
+
57
+ // Create reactor for your canister
58
+ export const backend = new Reactor<_SERVICE>({
59
+ clientManager,
60
+ idlFactory,
61
+ canisterId: "rrkah-fqaaa-aaaaa-aaaaq-cai",
62
+ })
35
63
  ```
36
64
 
37
- ### 2. Authentication (Auto-Initializes Session)
65
+ ### 2. Create Hooks
38
66
 
39
- The `useAuth` hook automatically initializes the agent and restores any previous session on first use.
67
+ ```typescript
68
+ // src/hooks.ts
69
+ import { createActorHooks, createAuthHooks } from "@ic-reactor/react"
70
+ import { backend, clientManager } from "./reactor"
71
+
72
+ // Create actor hooks for queries and mutations
73
+ export const { useActorQuery, useActorMutation, useActorSuspenseQuery } =
74
+ createActorHooks(backend)
75
+
76
+ // Create auth hooks for login/logout
77
+ export const { useAuth, useUserPrincipal } = createAuthHooks(clientManager)
78
+ ```
79
+
80
+ ### 3. Setup Provider (not required) and Use in Components
40
81
 
41
82
  ```tsx
42
83
  // src/App.tsx
43
- import { useAuth } from "./hooks/actor"
84
+ import { QueryClientProvider } from "@tanstack/react-query"
85
+ import { queryClient } from "./reactor"
86
+ import { useAuth, useActorQuery, useActorMutation } from "./hooks"
44
87
 
45
88
  function App() {
46
- // useAuth() auto-initializes - no separate setup needed!
47
- const { isAuthenticated, login, logout } = useAuth()
48
-
49
89
  return (
50
- <div>
51
- {isAuthenticated ? (
52
- <button onClick={logout}>Logout</button>
53
- ) : (
54
- <button onClick={login}>Login with II</button>
55
- )}
56
- <Dashboard />
57
- </div>
90
+ <QueryClientProvider client={queryClient}>
91
+ <AuthButton />
92
+ <Greeting />
93
+ </QueryClientProvider>
58
94
  )
59
95
  }
60
- ```
61
-
62
- ### 3. Use in Components
63
96
 
64
- Now you can use your hooks directly in your components!
97
+ function AuthButton() {
98
+ const { login, logout, isAuthenticated, principal } = useAuth()
65
99
 
66
- ```tsx
67
- // src/Dashboard.tsx
68
- import { useActorQuery, useActorMutation } from "./hooks/actor"
100
+ return isAuthenticated ? (
101
+ <button onClick={() => logout()}>
102
+ Logout {principal?.toText().slice(0, 8)}...
103
+ </button>
104
+ ) : (
105
+ <button onClick={() => login()}>Login with Internet Identity</button>
106
+ )
107
+ }
69
108
 
70
- function Dashboard() {
109
+ function Greeting() {
71
110
  // Query: Fetch data (auto-cached!)
72
- const { data: balance } = useActorQuery({
73
- functionName: "icrc1_balance_of",
74
- args: [{ owner: Principal.fromText("...") }],
111
+ const { data, isPending, error } = useActorQuery({
112
+ functionName: "greet",
113
+ args: ["World"],
75
114
  })
76
115
 
77
- // Update: Execute state changes
78
- const { mutate: transfer, isPending } = useActorMutation({
79
- functionName: "icrc1_transfer",
80
- onSuccess: () => {
81
- console.log("Transfer successful!")
82
- }
83
- })
116
+ if (isPending) return <div>Loading...</div>
117
+ if (error) return <div>Error: {error.message}</div>
84
118
 
85
- return (
86
- <div>
87
- <h1>Balance: {balance?.toString()}</h1>
88
- <button onClick={() => transfer(...)} disabled={isPending}>
89
- Transfer
90
- </button>
91
- </div>
92
- )
119
+ return <h1>{data}</h1>
93
120
  }
94
121
  ```
95
122
 
96
- ### Form Friendly (CandidAdapter)
123
+ ## Core Concepts
97
124
 
98
- If you want your hooks to automatically handle type transformations (e.g., converting `bigint` to `string` for simple form binding), set `autoCodecs: true`.
125
+ ### Reactor vs DisplayReactor
126
+
127
+ | Feature | `Reactor` | `DisplayReactor` |
128
+ | ------------- | ---------------- | --------------------------- |
129
+ | Types | Raw Candid types | Display-friendly types |
130
+ | BigInt | `bigint` | `string` |
131
+ | Principal | `Principal` | `string` |
132
+ | Vec nat8 | `Uint8Array` | <= 96 bytes: `string` (hex) |
133
+ | Result | Unwrapped | Unwrapped |
134
+ | Form-friendly | No | Yes |
99
135
 
100
136
  ```typescript
101
- export const { useActorQuery } = createActorHooks({
102
- canisterId,
137
+ import { DisplayReactor } from "@ic-reactor/react"
138
+
139
+ // DisplayReactor for form-friendly UI work
140
+ const backend = new DisplayReactor<_SERVICE>({
141
+ clientManager,
103
142
  idlFactory,
104
- autoCodecs: true, // Returns stringified values for bigint, principal, small blobs
143
+ canisterId: "rrkah-fqaaa-aaaaa-aaaaq-cai",
105
144
  })
145
+
146
+ // Now hooks return strings instead of bigint/Principal
147
+ const { data } = useActorQuery({
148
+ functionName: "icrc1_balance_of",
149
+ args: [{ owner: "aaaaa-aa", subaccount: [] }], // strings!
150
+ })
151
+ // data is "100000000" instead of 100000000n
106
152
  ```
107
153
 
108
- ### Authentication
154
+ ## Hooks Reference
109
155
 
110
- The `createActorHooks` function returns authentication hooks directly.
156
+ ### Actor Hooks (from `createActorHooks`)
111
157
 
112
- ```typescript
113
- import { useAuth } from "./hooks/actor"
158
+ | Hook | Description |
159
+ | ------------------------------- | ---------------------------------------------- |
160
+ | `useActorQuery` | Standard queries with loading states |
161
+ | `useActorSuspenseQuery` | Suspense-enabled queries (data always defined) |
162
+ | `useActorInfiniteQuery` | Paginated/infinite scroll queries |
163
+ | `useActorSuspenseInfiniteQuery` | Suspense infinite queries |
164
+ | `useActorMutation` | State-changing operations |
114
165
 
115
- function LoginButton() {
116
- const { login, logout, identity, isAuthenticated } = useAuth()
166
+ ### Auth Hooks (from `createAuthHooks`)
117
167
 
118
- return (
119
- <div>
120
- {isAuthenticated ? (
121
- <button onClick={logout}>Logout</button>
122
- ) : (
123
- <button onClick={login}>Login</button>
124
- )}
125
- </div>
126
- )
168
+ | Hook | Description |
169
+ | ------------------ | ----------------------------------- |
170
+ | `useAuth` | Login, logout, authentication state |
171
+ | `useAgentState` | Agent initialization state |
172
+ | `useUserPrincipal` | Current user's Principal |
173
+
174
+ ## Query Examples
175
+
176
+ ### Standard Query
177
+
178
+ ```tsx
179
+ const { data, isPending, error } = useActorQuery({
180
+ functionName: "get_user",
181
+ args: ["user-123"],
182
+ staleTime: 5 * 60 * 1000, // 5 minutes
183
+ })
184
+ ```
185
+
186
+ ### Suspense Query
187
+
188
+ ```tsx
189
+ // Parent must have <Suspense> boundary
190
+ function UserProfile() {
191
+ // data is never undefined with suspense!
192
+ const { data } = useActorSuspenseQuery({
193
+ functionName: "get_user",
194
+ args: ["user-123"],
195
+ })
196
+
197
+ return <div>{data.name}</div>
127
198
  }
128
199
  ```
129
200
 
130
- ### Dynamic Queries
201
+ ### Infinite Query
202
+
203
+ ```tsx
204
+ const { data, fetchNextPage, hasNextPage } = useActorInfiniteQuery({
205
+ functionName: "get_posts",
206
+ initialPageParam: 0,
207
+ getNextPageParam: (lastPage, pages) => pages.length * 10,
208
+ args: (pageParam) => [{ offset: pageParam, limit: 10 }],
209
+ })
210
+ ```
211
+
212
+ ## Mutation Examples
213
+
214
+ ### Basic Mutation
215
+
216
+ ```tsx
217
+ const { mutate, isPending, error } = useActorMutation({
218
+ functionName: "update_profile",
219
+ onSuccess: (result) => {
220
+ console.log("Profile updated!", result)
221
+ },
222
+ })
223
+
224
+ // Call the mutation
225
+ mutate([{ name: "Alice", bio: "Hello IC!" }])
226
+ ```
227
+
228
+ ## Query Factories
229
+
230
+ Create reusable query configurations with factory functions:
231
+
232
+ ```typescript
233
+ import {
234
+ createQuery,
235
+ createSuspenseQuery,
236
+ createMutation,
237
+ } from "@ic-reactor/react"
238
+
239
+ // Static query (no args at call time)
240
+ export const tokenNameQuery = createSuspenseQuery(backend, {
241
+ functionName: "icrc1_name",
242
+ })
243
+
244
+ // In component:
245
+ const { data } = tokenNameQuery.useSuspenseQuery()
246
+ ```
131
247
 
132
- Need to create queries on the fly? Use `createQuery`.
248
+ ### Factory with Dynamic Args
133
249
 
134
250
  ```typescript
135
- import { createQuery } from "@ic-reactor/react"
251
+ import { createSuspenseQueryFactory } from "@ic-reactor/react"
136
252
 
137
- const balanceQuery = createQuery(reactor, {
253
+ // Factory for balance queries
254
+ export const getBalance = createSuspenseQueryFactory(backend, {
138
255
  functionName: "icrc1_balance_of",
139
- select: (balance) => balance.toString() + " ICP",
256
+ select: (balance) => `${balance} tokens`,
140
257
  })
141
258
 
142
- const { data } = balanceQuery.useQuery()
259
+ // In component - pass args at call time
260
+ const { data } = getBalance.useSuspenseQuery({
261
+ args: [{ owner: userPrincipal, subaccount: [] }],
262
+ })
263
+ ```
264
+
265
+ ## Advanced: Direct Reactor Usage
266
+
267
+ Access reactor methods directly for manual cache management:
268
+
269
+ ```typescript
270
+ // Fetch and cache
271
+ await backend.fetchQuery({
272
+ functionName: "get_user",
273
+ args: ["user-123"],
274
+ })
275
+
276
+ // Get cached data (no fetch)
277
+ const cached = backend.getQueryData({
278
+ functionName: "get_user",
279
+ args: ["user-123"],
280
+ })
281
+
282
+ // Invalidate cache to trigger refetch
283
+ backend.invalidateQueries({
284
+ functionName: "get_user",
285
+ })
286
+
287
+ // Direct call without caching
288
+ const result = await backend.callMethod({
289
+ functionName: "update_user",
290
+ args: [{ name: "Alice" }],
291
+ })
292
+ ```
293
+
294
+ ## Re-exports
295
+
296
+ `@ic-reactor/react` re-exports everything from `@ic-reactor/core`, so you typically only need one import:
297
+
298
+ ```typescript
299
+ // Everything from one package
300
+ import {
301
+ ClientManager,
302
+ Reactor,
303
+ DisplayReactor,
304
+ createActorHooks,
305
+ createAuthHooks,
306
+ createQuery,
307
+ CanisterError,
308
+ } from "@ic-reactor/react"
143
309
  ```
144
310
 
311
+ ## Documentation
312
+
313
+ For comprehensive guides and API reference, visit the [documentation site](https://b3pay.github.io/ic-reactor/v3).
314
+
145
315
  ## License
146
316
 
147
- MIT
317
+ MIT © [Behrad Deylami](https://github.com/b3hr4d)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ic-reactor/react",
3
- "version": "3.0.0-beta.1",
3
+ "version": "3.0.0-beta.2",
4
4
  "description": "IC Reactor React Library",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",