@solana/react-hooks 1.0.0-rc.1 → 1.0.0-rc.3
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 +224 -340
- package/dist/index.browser.cjs +11 -5
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.mjs +11 -5
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.native.mjs +11 -5
- package/dist/index.native.mjs.map +1 -1
- package/dist/index.node.cjs +11 -5
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.mjs +11 -5
- package/dist/index.node.mjs.map +1 -1
- package/dist/types/hooks.d.ts +101 -7
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/query.d.ts +17 -0
- package/dist/types/query.d.ts.map +1 -1
- package/dist/types/queryHooks.d.ts +28 -0
- package/dist/types/queryHooks.d.ts.map +1 -1
- package/dist/types/ui.d.ts +33 -1
- package/dist/types/ui.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,432 +1,316 @@
|
|
|
1
1
|
# @solana/react-hooks
|
|
2
2
|
|
|
3
|
-
React hooks for `@solana/client`.
|
|
4
|
-
clients, wallets, and stores yourself.
|
|
5
|
-
|
|
6
|
-
> **Status:** Experimental – breaking changes may land often.
|
|
3
|
+
React hooks for `@solana/client`. Wrap your app once and reach for hooks instead of wiring RPC, wallets, and stores by hand.
|
|
7
4
|
|
|
8
5
|
## Install
|
|
9
6
|
|
|
10
7
|
```bash
|
|
11
|
-
|
|
8
|
+
npm install @solana/client @solana/react-hooks
|
|
12
9
|
```
|
|
13
10
|
|
|
14
|
-
##
|
|
11
|
+
## Quickstart
|
|
15
12
|
|
|
16
|
-
|
|
13
|
+
1. Choose wallet connectors (auto-discovery is the fastest way to start).
|
|
14
|
+
2. Create a Solana client.
|
|
15
|
+
3. Wrap your tree with `SolanaProvider` and use the hooks.
|
|
17
16
|
|
|
18
17
|
```tsx
|
|
19
|
-
import { autoDiscover,
|
|
20
|
-
import {
|
|
18
|
+
import { autoDiscover, createClient } from "@solana/client";
|
|
19
|
+
import {
|
|
20
|
+
SolanaProvider,
|
|
21
|
+
useBalance,
|
|
22
|
+
useWalletConnection,
|
|
23
|
+
} from "@solana/react-hooks";
|
|
21
24
|
|
|
22
|
-
const walletConnectors = [...phantom(), ...solflare(), ...backpack(), ...autoDiscover()];
|
|
23
25
|
const client = createClient({
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
walletConnectors,
|
|
26
|
+
endpoint: "https://api.devnet.solana.com",
|
|
27
|
+
walletConnectors: autoDiscover(),
|
|
27
28
|
});
|
|
28
29
|
|
|
29
|
-
function WalletButton() {
|
|
30
|
-
const connectWallet = useConnectWallet();
|
|
31
|
-
return <button onClick={() => connectWallet('phantom')}>Connect Phantom</button>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function WalletBalance() {
|
|
35
|
-
const wallet = useWallet();
|
|
36
|
-
const balance = useBalance(wallet.status === 'connected' ? wallet.session.account.address : undefined);
|
|
37
|
-
|
|
38
|
-
if (wallet.status !== 'connected') return <p>Connect a wallet</p>;
|
|
39
|
-
if (balance.fetching) return <p>Loading…</p>;
|
|
40
|
-
|
|
41
|
-
return <p>Lamports: {balance.lamports?.toString() ?? '0'}</p>;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
30
|
export function App() {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
);
|
|
31
|
+
return (
|
|
32
|
+
<SolanaProvider client={client}>
|
|
33
|
+
{/* your components that call hooks go here */}
|
|
34
|
+
</SolanaProvider>
|
|
35
|
+
);
|
|
51
36
|
}
|
|
52
37
|
```
|
|
53
38
|
|
|
54
|
-
|
|
55
|
-
(`revalidateOnFocus`/`revalidateOnReconnect`/`revalidateIfStale` are `true`, `dedupingInterval` is `2000`,
|
|
56
|
-
`focusThrottleInterval` is `5000`). Override them via the `query.config` prop or per-hook `swr` options.
|
|
57
|
-
Prefer passing a `client`; `config`-based setup on `SolanaClientProvider` is still available for bespoke
|
|
58
|
-
composition.
|
|
59
|
-
|
|
60
|
-
Every hook exposes `UseHookNameParameters` / `UseHookNameReturnType` aliases so wrapper components stay in
|
|
61
|
-
sync with the public API.
|
|
62
|
-
|
|
63
|
-
## Hooks at a glance
|
|
39
|
+
> **Next.js / RSC:** Components that call these hooks must be marked with `'use client'`.
|
|
64
40
|
|
|
65
|
-
|
|
66
|
-
- `useBalance` / `useAccount` – fetch lamports once or keep account data in sync.
|
|
67
|
-
- `useSolTransfer`, `useSplToken`, `useTransactionPool` – helper-driven flows for SOL, SPL, and
|
|
68
|
-
general transactions.
|
|
69
|
-
- `useSendTransaction` – prepare and submit arbitrary instructions with shared mutation state.
|
|
70
|
-
- `useSignatureStatus`, `useWaitForSignature` – declarative helpers for tracking confirmations.
|
|
71
|
-
- `useClientStore` – access the underlying Zustand store if you need low-level state.
|
|
41
|
+
## Common Solana flows (copy/paste)
|
|
72
42
|
|
|
73
|
-
|
|
43
|
+
These snippets assume a parent already handled wallet connection and can pass an address where needed.
|
|
74
44
|
|
|
75
|
-
|
|
45
|
+
### Connect, disconnect, and show balance
|
|
76
46
|
|
|
77
47
|
```tsx
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (wallet.status === 'connected') {
|
|
84
|
-
return (
|
|
85
|
-
<div>
|
|
86
|
-
<p>{wallet.session.account.address.toString()}</p>
|
|
87
|
-
<button onClick={() => disconnect()}>Disconnect</button>
|
|
88
|
-
</div>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return <button onClick={() => connect('phantom')}>Connect Phantom</button>;
|
|
93
|
-
};
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Balance watcher
|
|
97
|
-
|
|
98
|
-
Read lamports (cached plus live updates) for any address.
|
|
99
|
-
|
|
100
|
-
```tsx
|
|
101
|
-
import { useBalance } from '@solana/react-hooks';
|
|
48
|
+
function WalletPanel() {
|
|
49
|
+
const { connectors, connect, disconnect, wallet, status } =
|
|
50
|
+
useWalletConnection();
|
|
51
|
+
const address = wallet?.account.address;
|
|
52
|
+
const balance = useBalance(address);
|
|
102
53
|
|
|
103
|
-
|
|
104
|
-
const { lamports, fetching, slot } = useBalance(address);
|
|
105
|
-
if (fetching) return <p>Loading…</p>;
|
|
54
|
+
if (status === "connected") {
|
|
106
55
|
return (
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
</
|
|
56
|
+
<div>
|
|
57
|
+
<p>{address?.toString()}</p>
|
|
58
|
+
<p>Lamports: {balance.lamports?.toString() ?? "loading…"}</p>
|
|
59
|
+
<button onClick={disconnect}>Disconnect</button>
|
|
60
|
+
</div>
|
|
111
61
|
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return connectors.map((c) => (
|
|
65
|
+
<button key={c.id} onClick={() => connect(c.id)}>
|
|
66
|
+
Connect {c.name}
|
|
67
|
+
</button>
|
|
68
|
+
));
|
|
112
69
|
}
|
|
113
70
|
```
|
|
114
71
|
|
|
115
|
-
###
|
|
116
|
-
|
|
117
|
-
Fetch account data and optionally keep it in sync via subscriptions.
|
|
72
|
+
### Read lamport balance (auto fetch + watch)
|
|
118
73
|
|
|
119
74
|
```tsx
|
|
120
|
-
import {
|
|
121
|
-
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
75
|
+
import { useBalance } from "@solana/react-hooks";
|
|
76
|
+
|
|
77
|
+
function BalanceCard({ address }: { address: string }) {
|
|
78
|
+
const { lamports, fetching, slot } = useBalance(address);
|
|
79
|
+
if (fetching) return <p>Loading…</p>;
|
|
80
|
+
return (
|
|
81
|
+
<p>
|
|
82
|
+
Lamports: {lamports?.toString() ?? "0"} (slot {slot?.toString() ?? "—"})
|
|
83
|
+
</p>
|
|
84
|
+
);
|
|
129
85
|
}
|
|
130
86
|
```
|
|
131
87
|
|
|
132
|
-
### SOL
|
|
133
|
-
|
|
134
|
-
Trigger SOL transfers with built-in status tracking.
|
|
88
|
+
### Send SOL
|
|
135
89
|
|
|
136
90
|
```tsx
|
|
137
|
-
import { useSolTransfer } from
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
91
|
+
import { useSolTransfer } from "@solana/react-hooks";
|
|
92
|
+
|
|
93
|
+
function SendSol({ destination }: { destination: string }) {
|
|
94
|
+
const { send, isSending, status, signature, error } = useSolTransfer(); // expects a connected wallet
|
|
95
|
+
return (
|
|
96
|
+
<div>
|
|
97
|
+
<button
|
|
98
|
+
disabled={isSending}
|
|
99
|
+
onClick={() =>
|
|
100
|
+
send({ destination, lamports: 100_000_000n /* 0.1 SOL */ })
|
|
101
|
+
}
|
|
102
|
+
>
|
|
103
|
+
{isSending ? "Sending…" : "Send 0.1 SOL"}
|
|
104
|
+
</button>
|
|
105
|
+
<p>Status: {status}</p>
|
|
106
|
+
{signature ? <p>Signature: {signature}</p> : null}
|
|
107
|
+
{error ? <p role="alert">Error: {String(error)}</p> : null}
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
156
111
|
```
|
|
157
112
|
|
|
158
|
-
### SPL
|
|
159
|
-
|
|
160
|
-
Scope SPL helpers by mint and reuse the same API for balances and transfers.
|
|
113
|
+
### SPL token balance + transfer
|
|
161
114
|
|
|
162
115
|
```tsx
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
116
|
+
import { useSplToken } from "@solana/react-hooks";
|
|
117
|
+
|
|
118
|
+
function TokenPanel({
|
|
119
|
+
mint,
|
|
120
|
+
destinationOwner,
|
|
121
|
+
}: {
|
|
122
|
+
mint: string;
|
|
123
|
+
destinationOwner: string;
|
|
124
|
+
}) {
|
|
125
|
+
const { balance, send, isSending, owner } = useSplToken(mint);
|
|
126
|
+
return (
|
|
127
|
+
<div>
|
|
128
|
+
<p>Owner: {owner ?? "Connect wallet"}</p>
|
|
129
|
+
<p>Balance: {balance?.uiAmount ?? "0"}</p>
|
|
130
|
+
<button
|
|
131
|
+
disabled={isSending || !owner}
|
|
132
|
+
onClick={() => send({ amount: 1n, destinationOwner })}
|
|
133
|
+
>
|
|
134
|
+
{isSending ? "Sending…" : "Send 1 token"}
|
|
135
|
+
</button>
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
183
139
|
```
|
|
184
140
|
|
|
185
|
-
###
|
|
186
|
-
|
|
187
|
-
Compose instructions, refresh blockhashes automatically, and send transactions from one hook.
|
|
141
|
+
### Build and send arbitrary transactions
|
|
188
142
|
|
|
189
143
|
```tsx
|
|
190
|
-
import type { TransactionInstructionInput } from
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
} = useTransactionPool();
|
|
206
|
-
|
|
207
|
-
return (
|
|
208
|
-
<div>
|
|
209
|
-
<button onClick={() => addInstruction(instruction)}>Add instruction</button>
|
|
210
|
-
<button disabled={sendStatus === 'loading'} onClick={() => prepareAndSend()}>
|
|
211
|
-
{sendStatus === 'loading' ? 'Sending…' : 'Prepare & Send'}
|
|
212
|
-
</button>
|
|
213
|
-
<p>Recent blockhash: {latestBlockhash.blockhash ?? 'loading…'}</p>
|
|
214
|
-
</div>
|
|
215
|
-
);
|
|
216
|
-
};
|
|
144
|
+
import type { TransactionInstructionInput } from "@solana/client";
|
|
145
|
+
import { useTransactionPool } from "@solana/react-hooks";
|
|
146
|
+
|
|
147
|
+
function TransactionFlow({ ix }: { ix: TransactionInstructionInput }) {
|
|
148
|
+
const pool = useTransactionPool();
|
|
149
|
+
return (
|
|
150
|
+
<div>
|
|
151
|
+
<button onClick={() => pool.addInstruction(ix)}>Add instruction</button>
|
|
152
|
+
<button disabled={pool.isSending} onClick={() => pool.prepareAndSend()}>
|
|
153
|
+
{pool.isSending ? "Sending…" : "Prepare & Send"}
|
|
154
|
+
</button>
|
|
155
|
+
<p>Blockhash: {pool.latestBlockhash.blockhash ?? "loading…"}</p>
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
217
159
|
```
|
|
218
160
|
|
|
219
|
-
###
|
|
220
|
-
|
|
221
|
-
Drop down to the underlying Zustand store when you need bespoke selectors.
|
|
161
|
+
### Simple mutation helper (when you already have instructions)
|
|
222
162
|
|
|
223
163
|
```tsx
|
|
224
|
-
import {
|
|
225
|
-
|
|
226
|
-
function
|
|
227
|
-
|
|
228
|
-
|
|
164
|
+
import { useSendTransaction } from "@solana/react-hooks";
|
|
165
|
+
|
|
166
|
+
function SendPrepared({ instructions }) {
|
|
167
|
+
const { send, isSending, signature, error } = useSendTransaction();
|
|
168
|
+
return (
|
|
169
|
+
<div>
|
|
170
|
+
<button disabled={isSending} onClick={() => send({ instructions })}>
|
|
171
|
+
{isSending ? "Submitting…" : "Send transaction"}
|
|
172
|
+
</button>
|
|
173
|
+
{signature ? <p>Signature: {signature}</p> : null}
|
|
174
|
+
{error ? <p role="alert">{String(error)}</p> : null}
|
|
175
|
+
</div>
|
|
176
|
+
);
|
|
229
177
|
}
|
|
230
178
|
```
|
|
231
179
|
|
|
232
|
-
###
|
|
233
|
-
|
|
234
|
-
Use `useSendTransaction` when you already have instructions/messages and just need a mutation helper
|
|
235
|
-
that exposes `{ send, sendPrepared, status, error, signature }`. When no authority is supplied, it
|
|
236
|
-
will use the currently connected wallet session by default.
|
|
180
|
+
### Track confirmations for a signature
|
|
237
181
|
|
|
238
182
|
```tsx
|
|
239
|
-
import {
|
|
240
|
-
|
|
241
|
-
function
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
{isSending ? 'Submitting…' : 'Send transaction'}
|
|
248
|
-
</button>
|
|
249
|
-
{signature ? <p>Signature: {signature}</p> : null}
|
|
250
|
-
{error ? <p role="alert">Failed to send: {String(error)}</p> : null}
|
|
251
|
-
</div>
|
|
252
|
-
);
|
|
183
|
+
import { useWaitForSignature } from "@solana/react-hooks";
|
|
184
|
+
|
|
185
|
+
function SignatureWatcher({ signature }: { signature: string }) {
|
|
186
|
+
const wait = useWaitForSignature(signature, { commitment: "finalized" });
|
|
187
|
+
if (wait.waitStatus === "error") return <p role="alert">Failed</p>;
|
|
188
|
+
if (wait.waitStatus === "success") return <p>Finalized ✅</p>;
|
|
189
|
+
if (wait.waitStatus === "waiting") return <p>Waiting…</p>;
|
|
190
|
+
return <p>Provide a signature</p>;
|
|
253
191
|
}
|
|
254
192
|
```
|
|
255
193
|
|
|
256
|
-
###
|
|
257
|
-
|
|
258
|
-
Poll RPC for signature metadata or wait for a confirmation level without writing loops.
|
|
194
|
+
### Query program accounts
|
|
259
195
|
|
|
260
196
|
```tsx
|
|
261
|
-
import {
|
|
262
|
-
|
|
263
|
-
function
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
197
|
+
import { SolanaQueryProvider, useProgramAccounts } from "@solana/react-hooks";
|
|
198
|
+
|
|
199
|
+
function ProgramAccounts({ program }: { program: string }) {
|
|
200
|
+
const query = useProgramAccounts(program);
|
|
201
|
+
if (query.isLoading) return <p>Loading…</p>;
|
|
202
|
+
if (query.isError) return <p role="alert">RPC error</p>;
|
|
203
|
+
return (
|
|
204
|
+
<div>
|
|
205
|
+
<button onClick={() => query.refresh()}>Refresh</button>
|
|
206
|
+
<ul>
|
|
207
|
+
{query.accounts.map(({ pubkey }) => (
|
|
208
|
+
<li key={pubkey.toString()}>{pubkey.toString()}</li>
|
|
209
|
+
))}
|
|
210
|
+
</ul>
|
|
211
|
+
</div>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
268
214
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
215
|
+
function ProgramAccountsSection({ program }: { program: string }) {
|
|
216
|
+
return (
|
|
217
|
+
<SolanaQueryProvider>
|
|
218
|
+
<ProgramAccounts program={program} />
|
|
219
|
+
</SolanaQueryProvider>
|
|
220
|
+
);
|
|
275
221
|
}
|
|
222
|
+
```
|
|
276
223
|
|
|
277
|
-
|
|
278
|
-
const wait = useWaitForSignature(signature, { commitment: 'finalized' });
|
|
224
|
+
### Simulate a transaction
|
|
279
225
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
226
|
+
```tsx
|
|
227
|
+
import { useSimulateTransaction } from "@solana/react-hooks";
|
|
228
|
+
|
|
229
|
+
function Simulation({ wire }: { wire: string }) {
|
|
230
|
+
const sim = useSimulateTransaction(wire);
|
|
231
|
+
if (sim.isLoading) return <p>Simulating…</p>;
|
|
232
|
+
if (sim.isError) return <p role="alert">Simulation failed</p>;
|
|
233
|
+
return (
|
|
234
|
+
<div>
|
|
235
|
+
<button onClick={() => sim.refresh()}>Re-run</button>
|
|
236
|
+
<pre>{JSON.stringify(sim.logs, null, 2)}</pre>
|
|
237
|
+
</div>
|
|
238
|
+
);
|
|
284
239
|
}
|
|
285
240
|
```
|
|
286
241
|
|
|
287
|
-
##
|
|
288
|
-
|
|
289
|
-
Wrap a subtree with `<SolanaQueryProvider>` and call hooks like `useLatestBlockhash`,
|
|
290
|
-
`useProgramAccounts`, `useSignatureStatus`, or `useSimulateTransaction`. Every hook returns
|
|
291
|
-
`{ data, status, refresh }` so you can read the current value and trigger a refetch. Want to lean on React
|
|
292
|
-
Suspense later? Pass `suspense` to `SolanaQueryProvider` and wrap just the section that should pause in a
|
|
293
|
-
local `<Suspense>` boundary—no hook changes required:
|
|
242
|
+
## Using Suspense (opt-in)
|
|
294
243
|
|
|
295
|
-
|
|
296
|
-
are `true`, `dedupingInterval` is `2000`, and `focusThrottleInterval` is `5000`. Override them per-provider via the
|
|
297
|
-
`query.config` prop or per-hook via the `swr` option.
|
|
244
|
+
Enable Suspense per subtree by setting `suspense` on `SolanaQueryProvider` and wrapping content in a React `<Suspense>` boundary. This keeps the rest of the UI non-blocking.
|
|
298
245
|
|
|
299
246
|
```tsx
|
|
300
|
-
import { SolanaQueryProvider, useBalance } from
|
|
301
|
-
import { Suspense } from
|
|
247
|
+
import { SolanaQueryProvider, useBalance } from "@solana/react-hooks";
|
|
248
|
+
import { Suspense } from "react";
|
|
302
249
|
|
|
303
250
|
function BalanceDetails({ address }: { address: string }) {
|
|
304
|
-
|
|
305
|
-
|
|
251
|
+
const balance = useBalance(address);
|
|
252
|
+
return <p>Lamports: {balance.lamports?.toString() ?? "0"}</p>;
|
|
306
253
|
}
|
|
307
254
|
|
|
308
255
|
export function WalletPanel({ address }: { address: string }) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
);
|
|
256
|
+
return (
|
|
257
|
+
<SolanaQueryProvider suspense>
|
|
258
|
+
<Suspense fallback={<p>Loading balance…</p>}>
|
|
259
|
+
<BalanceDetails address={address} />
|
|
260
|
+
</Suspense>
|
|
261
|
+
</SolanaQueryProvider>
|
|
262
|
+
);
|
|
317
263
|
}
|
|
318
264
|
```
|
|
319
265
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
Poll or refetch the cluster's latest blockhash.
|
|
266
|
+
## Provider SWR config (optional)
|
|
323
267
|
|
|
324
268
|
```tsx
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
export function BlockhashCard() {
|
|
342
|
-
return (
|
|
343
|
-
<SolanaQueryProvider>
|
|
344
|
-
<BlockhashTicker />
|
|
345
|
-
</SolanaQueryProvider>
|
|
346
|
-
);
|
|
269
|
+
export function App() {
|
|
270
|
+
return (
|
|
271
|
+
<SolanaProvider
|
|
272
|
+
client={client}
|
|
273
|
+
query={{
|
|
274
|
+
config: {
|
|
275
|
+
revalidateOnFocus: false,
|
|
276
|
+
revalidateOnReconnect: false,
|
|
277
|
+
refreshInterval: 30_000,
|
|
278
|
+
},
|
|
279
|
+
}}
|
|
280
|
+
>
|
|
281
|
+
<WalletPanel />
|
|
282
|
+
</SolanaProvider>
|
|
283
|
+
);
|
|
347
284
|
}
|
|
348
285
|
```
|
|
349
286
|
|
|
350
|
-
|
|
287
|
+
Defaults when you omit `query.config`:
|
|
288
|
+
- `revalidateOnFocus` / `revalidateOnReconnect` / `revalidateIfStale`: `true`
|
|
289
|
+
- `dedupingInterval`: `2000ms`
|
|
290
|
+
- `focusThrottleInterval`: `5000ms`
|
|
351
291
|
|
|
352
|
-
|
|
353
|
-
import { autoDiscover, backpack, createClient, phantom, solflare } from '@solana/client';
|
|
354
|
-
import { SolanaProvider, SolanaQueryProvider, useProgramAccounts } from '@solana/react-hooks';
|
|
292
|
+
SWR background: stale-while-revalidate (RFC 5861): https://datatracker.ietf.org/doc/html/rfc5861
|
|
355
293
|
|
|
356
|
-
|
|
357
|
-
const client = createClient({
|
|
358
|
-
endpoint: 'https://api.devnet.solana.com',
|
|
359
|
-
websocketEndpoint: 'wss://api.devnet.solana.com',
|
|
360
|
-
walletConnectors,
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
function ProgramAccountsList({ programAddress }) {
|
|
364
|
-
const query = useProgramAccounts(programAddress);
|
|
365
|
-
if (query.status === 'loading') return <p>Loading accounts…</p>;
|
|
366
|
-
if (query.status === 'error') return <p role="alert">Retry later.</p>;
|
|
294
|
+
### Work with the client store directly
|
|
367
295
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
<button onClick={() => query.refresh()}>Refresh</button>
|
|
371
|
-
<p>Status: {query.status}</p>
|
|
372
|
-
<ul>
|
|
373
|
-
{query.accounts?.map(({ pubkey }) => (
|
|
374
|
-
<li key={pubkey.toString()}>{pubkey.toString()}</li>
|
|
375
|
-
))}
|
|
376
|
-
</ul>
|
|
377
|
-
</div>
|
|
378
|
-
);
|
|
379
|
-
}
|
|
296
|
+
```tsx
|
|
297
|
+
import { useClientStore } from "@solana/react-hooks";
|
|
380
298
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
<SolanaQueryProvider>
|
|
385
|
-
<ProgramAccountsList programAddress={programAddress} />
|
|
386
|
-
</SolanaQueryProvider>
|
|
387
|
-
</SolanaProvider>
|
|
388
|
-
);
|
|
299
|
+
function ClusterBadge() {
|
|
300
|
+
const cluster = useClientStore((s) => s.cluster);
|
|
301
|
+
return <p>Endpoint: {cluster.endpoint}</p>;
|
|
389
302
|
}
|
|
390
303
|
```
|
|
391
304
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
Simulate any transaction payload (wire string or object) and read RPC logs.
|
|
305
|
+
## Notes and defaults
|
|
395
306
|
|
|
396
|
-
|
|
397
|
-
|
|
307
|
+
- Wallet connectors: use `autoDiscover()` to pick up Wallet Standard injectables; or explicitly compose `phantom()`, `solflare()`, `backpack()`, etc.
|
|
308
|
+
- Queries: all RPC query hooks accept `swr` options under `swr` and `disabled` flags. Suspense is opt-in via `SolanaQueryProvider`’s `suspense` prop.
|
|
309
|
+
- Authorities: transaction helpers default to the connected wallet session when `authority` is omitted.
|
|
310
|
+
- Types: every hook exports `UseHookNameParameters` / `UseHookNameReturnType` aliases.
|
|
398
311
|
|
|
399
|
-
|
|
400
|
-
const query = useSimulateTransaction(transaction);
|
|
401
|
-
if (query.status === 'loading') return <p>Simulating…</p>;
|
|
402
|
-
if (query.status === 'error') return <p role="alert">Simulation failed.</p>;
|
|
403
|
-
return (
|
|
404
|
-
<div>
|
|
405
|
-
<button onClick={() => query.refresh()}>Re-run</button>
|
|
406
|
-
<p>Status: {query.status}</p>
|
|
407
|
-
<pre>{JSON.stringify(query.logs, null, 2)}</pre>
|
|
408
|
-
</div>
|
|
409
|
-
);
|
|
410
|
-
}
|
|
411
|
-
```
|
|
312
|
+
## More resources
|
|
412
313
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
-
|
|
416
|
-
helpers, and the connector list from `client.connectors` (or an explicit override). Pair it with
|
|
417
|
-
your preferred UI, or `WalletConnectionManager` for a simple modal state helper.
|
|
418
|
-
- Signing helpers: the wallet session returned by `useWallet` exposes `signMessage`,
|
|
419
|
-
`signTransaction`, and `sendTransaction` when supported by the connector. These connector methods
|
|
420
|
-
replace the deprecated Wallet Standard shims.
|
|
421
|
-
- Query hooks keep SWR options under `swr` for consistency (for example,
|
|
422
|
-
`useProgramAccounts(address, { swr: { revalidateOnFocus: false } })`) and expose typed parameter
|
|
423
|
-
and return aliases across all hooks.
|
|
424
|
-
- Type helpers: use `UseHookNameParameters` / `UseHookNameReturnType` for public hooks.
|
|
425
|
-
- Looking for examples? See `examples/vite-react` for a ready-to-run, tabbed playground that wires
|
|
426
|
-
the provider, hooks, and mock UIs together across wallet/state, transaction, and query demos.
|
|
427
|
-
|
|
428
|
-
## Scripts
|
|
429
|
-
|
|
430
|
-
- `pnpm build` – run both JS compilation and type definition emit
|
|
431
|
-
- `pnpm test:typecheck` – strict type-checking without emit
|
|
432
|
-
- `pnpm lint` / `pnpm format` – Biome-powered linting and formatting
|
|
314
|
+
- Playground: `examples/vite-react` (run with `pnpm install && pnpm dev`).
|
|
315
|
+
- Next.js reference app: `examples/nextjs`.
|
|
316
|
+
- Hook JSDoc lives in `src/hooks.ts`, `src/queryHooks.ts`, `src/ui.tsx`.
|