Skip to content

Commit b0480c0

Browse files
committed
Add useTrackedDataSwr to the @solana/react/swr adapter
SWR-backed counterpart to `useTrackedData`. Takes the same `TrackedDataSpec` (RPC fetch + subscription pair + value mappers) and routes the unified, slot-deduped stream through SWR's `useSWRSubscription`, building a fresh `createReactiveStoreWithInitialValueAndSlotTracking` per subscription. Returns the same `SlotTaggedValue<TItem>` shape that `useSubscriptionSwr` exports — `data.value` is the unified item produced by the spec's mappers; `data.slot` is the envelope's `context.slot`. Mirrors core `useTrackedData`'s top-level `{ data, slot }` flattened into SWR's `data` slot, and matches `useSubscriptionSwr` so a switch between the two SWR hooks is just a different generic, not a different access pattern. Either `key === null` or `spec === null` disables the subscription, mirroring `useTrackedData`'s nullable-spec pattern. Options merge SWR's config with `useTrackedData`'s `UseTrackedDataOptions` (`getAbortSignal` for per-attempt signals threaded into the underlying store via `withSignal(signal).connect()`).
1 parent b1c4037 commit b0480c0

6 files changed

Lines changed: 660 additions & 0 deletions

File tree

.changeset/neat-coats-boil.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@solana/react': minor
3+
---
4+
5+
Add `useTrackedDataSWR(key, spec, options?)` to the `@solana/react/swr` subpath — the SWR-backed counterpart to `useTrackedData`. Takes the same `TrackedDataSpec` and routes the unified, slot-deduped stream through SWR's `useSWRSubscription`.
6+
7+
```tsx
8+
import { useTrackedDataSWR } from '@solana/react/swr';
9+
10+
const { data } = useTrackedDataSWR(['balance', address], spec);
11+
// data is `SolanaRpcResponse<TItem> | undefined`
12+
```
13+
14+
`data` is shape `SolanaRpcResponse<TItem>`, because this hook requires the slot for de-duping. Mirrors core `useTrackedData`. Pass `null` for either `key` or `spec` to disable. Options accept SWR's config plus `getAbortSignal` for a custom abort signal.

packages/react/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,32 @@ function AccountBalance({ address }: { address: Address }) {
377377

378378
If the `source` changes (new address, new notification type) but the SWR `key` is stable, the existing connection stays bound to the original source — SWR caches on `key`, and `subscribe` reads the source from a ref. Bump the `key` to swap sources.
379379

380+
### `useTrackedDataSWR(key, spec, options?)`
381+
382+
SWR-backed counterpart to `useTrackedData`. Takes the same `TrackedDataSpec` (RPC fetch + subscription pair + value mappers) and routes the unified, slot-deduped stream through SWR. Returns SWR's native `{ data, error }` shape — `data` is the `SolanaRpcResponse<TItem>` envelope emitted by the underlying kit primitive, so callers can read `data.value` (the unified item produced by the mappers) and `data.context.slot` (the slot the store dedup'd on) directly. Pass `null` for `key` or `spec` to disable. SWR subscriptions surface only `{ data, error }`, so there is no `refresh` function like `useTrackedData` has — reach for `useTrackedData` when you need manual refresh. For the same reason `getAbortSignal` is not available.
383+
384+
```tsx
385+
function AccountBalance({ address }: { address: Address }) {
386+
const client = useClient<ClientWithRpc<GetBalanceApi> & ClientWithRpcSubscriptions<AccountNotificationsApi>>();
387+
const spec = useMemo(
388+
() =>
389+
address
390+
? {
391+
initialValueSource: client.rpc.getBalance(address),
392+
initialValueMapper: (lamports: bigint) => lamports,
393+
streamSource: client.rpcSubscriptions.accountNotifications(address),
394+
streamValueMapper: ({ lamports }: { lamports: bigint }) => lamports,
395+
}
396+
: null,
397+
[client, address],
398+
);
399+
const { data } = useTrackedDataSWR(address ? ['balance', address] : null, spec);
400+
return <p>{data ? `${data.value} lamports at slot ${data.context.slot}` : 'Loading…'}</p>;
401+
}
402+
```
403+
404+
If the `spec` changes (new mappers, new source) but the SWR `key` is stable, the existing connection stays bound to the original spec — SWR caches on `key`, and `subscribe` reads the spec from a ref. Bump the `key` to swap specs.
405+
380406
### Why no `useActionSWR`?
381407

382408
It would just be a wrapper around SWR's built-in [`useSWRMutation`](https://swr.vercel.app/docs/mutation#useswrmutation) with no additional functionality. Either use `useSWRMutation` or, if you don't need the SWR integration, use `useAction`.

packages/react/src/swr.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
*/
1212
export * from './swr/useRequestSWR';
1313
export * from './swr/useSubscriptionSWR';
14+
export * from './swr/useTrackedDataSWR';

0 commit comments

Comments
 (0)