Skip to content

Add useTrackedDataSwr to the @solana/react/swr adapter#1727

Open
mcintyre94 wants to merge 1 commit into
react/use-subscription-swrfrom
react/use-tracked-data-swr
Open

Add useTrackedDataSwr to the @solana/react/swr adapter#1727
mcintyre94 wants to merge 1 commit into
react/use-subscription-swrfrom
react/use-tracked-data-swr

Conversation

@mcintyre94

@mcintyre94 mcintyre94 commented Jun 5, 2026

Copy link
Copy Markdown
Member

Summary of Changes

This PR adds useTrackedDataSWR, the SWR shaped version of useTrackedData.

function AccountBalance({ address }: { address: Address }) {
    const client = useClient<ClientWithRpc<GetBalanceApi> & ClientWithRpcSubscriptions<AccountNotificationsApi>>();
    const spec = useMemo(
        () =>
            address
                ? {
                      initialValueSource: client.rpc.getBalance(address),
                      initialValueMapper: (lamports: bigint) => lamports,
                      streamSource: client.rpcSubscriptions.accountNotifications(address),
                      streamValueMapper: ({ lamports }: { lamports: bigint }) => lamports,
                  }
                : null,
        [client, address],
    );
    const { data } = useTrackedDataSWR(address ? ['balance', address] : null, spec);
    return <p>{data ? `${data.value} lamports at slot ${data.context.slot}` : 'Loading…'}</p>;
}

As with the other SWR hooks the third param is an SWRConfiguration, with our usual getAbortSignal factory to add a custom signal. It also disables when either key or source are null. Note that SWR subscriptions don't include the reconnect function from useTrackedData.

Note that under the hood this uses useSWRSubscription, which enables cleaning up the subscription and resetting the store.

Note that the issue from useSubscriptionSWR around forwarding initial store state does not apply here, because this hook creates its own store combining the two inputs (using createReactiveStoreWithInitialValueAndSlotTracking from Kit).

@changeset-bot

changeset-bot Bot commented Jun 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b0480c0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 47 packages
Name Type
@solana/react Major
@solana/accounts Major
@solana/addresses Major
@solana/assertions Major
@solana/codecs-core Major
@solana/codecs-data-structures Major
@solana/codecs-numbers Major
@solana/codecs-strings Major
@solana/codecs Major
@solana/compat Major
@solana/errors Major
@solana/fast-stable-stringify Major
@solana/fixed-points Major
@solana/functional Major
@solana/instruction-plans Major
@solana/instructions Major
@solana/keys Major
@solana/kit Major
@solana/nominal-types Major
@solana/offchain-messages Major
@solana/options Major
@solana/plugin-core Major
@solana/plugin-interfaces Major
@solana/program-client-core Major
@solana/programs Major
@solana/promises Major
@solana/rpc-api Major
@solana/rpc-graphql Major
@solana/rpc-parsed-types Major
@solana/rpc-spec-types Major
@solana/rpc-spec Major
@solana/rpc-subscriptions-api Major
@solana/rpc-subscriptions-channel-websocket Major
@solana/rpc-subscriptions-spec Major
@solana/rpc-subscriptions Major
@solana/rpc-transformers Major
@solana/rpc-transport-http Major
@solana/rpc-types Major
@solana/rpc Major
@solana/signers Major
@solana/subscribable Major
@solana/sysvars Major
@solana/transaction-confirmation Major
@solana/transaction-messages Major
@solana/transactions Major
@solana/wallet-account-signer Major
@solana/webcrypto-ed25519-polyfill Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

mcintyre94 commented Jun 5, 2026

Copy link
Copy Markdown
Member Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@bundlemon

bundlemon Bot commented Jun 5, 2026

Copy link
Copy Markdown

BundleMon

Files updated (25)
Status Path Size Limits
react/dist/index.browser.mjs
5.06KB (+1.96KB +63.36%) -
react/dist/index.native.mjs
5.05KB (+1.96KB +63.38%) -
react/dist/index.node.mjs
5.05KB (+1.96KB +63.4%) -
@solana/kit production bundle
kit/dist/index.production.min.js
53.03KB (+429B +0.8%) -
kit/dist/index.browser.mjs
4.81KB (+349B +7.62%) -
kit/dist/index.native.mjs
4.81KB (+349B +7.63%) -
kit/dist/index.node.mjs
4.81KB (+349B +7.63%) -
errors/dist/index.browser.mjs
20.9KB (+179B +0.84%) -
errors/dist/index.native.mjs
20.9KB (+179B +0.84%) -
errors/dist/index.node.mjs
20.92KB (+179B +0.84%) -
wallet-account-signer/dist/index.browser.mjs
17.69KB (+169B +0.94%) -
wallet-account-signer/dist/index.native.mjs
17.69KB (+168B +0.94%) -
wallet-account-signer/dist/index.node.mjs
17.71KB (+168B +0.93%) -
subscribable/dist/index.browser.mjs
2.76KB (+160B +6%) -
subscribable/dist/index.native.mjs
2.77KB (+160B +5.99%) -
subscribable/dist/index.node.mjs
2.83KB (+158B +5.76%) -
rpc-types/dist/index.browser.mjs
1.9KB (+102B +5.53%) -
rpc-types/dist/index.native.mjs
1.9KB (+101B +5.48%) -
rpc-types/dist/index.node.mjs
1.9KB (+101B +5.48%) -
rpc-subscriptions-spec/dist/index.browser.mjs
2.19KB (-13B -0.58%) -
rpc-subscriptions-spec/dist/index.native.mjs
2.19KB (-13B -0.58%) -
rpc-subscriptions-spec/dist/index.node.mjs
2.23KB (-13B -0.56%) -
rpc-spec/dist/index.browser.mjs
898B (-20B -2.18%) -
rpc-spec/dist/index.native.mjs
897B (-21B -2.29%) -
rpc-spec/dist/index.node.mjs
896B (-21B -2.29%) -
Unchanged files (122)
Status Path Size Limits
rpc-graphql/dist/index.browser.mjs
18.82KB -
rpc-graphql/dist/index.native.mjs
18.81KB -
rpc-graphql/dist/index.node.mjs
18.81KB -
transaction-messages/dist/index.browser.mjs
11.32KB -
transaction-messages/dist/index.native.mjs
11.32KB -
transaction-messages/dist/index.node.mjs
11.32KB -
instruction-plans/dist/index.browser.mjs
6.58KB -
instruction-plans/dist/index.native.mjs
6.58KB -
instruction-plans/dist/index.node.mjs
6.58KB -
fixed-points/dist/index.browser.mjs
5.08KB -
fixed-points/dist/index.native.mjs
5.07KB -
fixed-points/dist/index.node.mjs
5.07KB -
codecs-data-structures/dist/index.browser.mjs
5.04KB -
codecs-data-structures/dist/index.native.mjs
5.03KB -
codecs-data-structures/dist/index.node.mjs
5.03KB -
offchain-messages/dist/index.browser.mjs
4.89KB -
offchain-messages/dist/index.native.mjs
4.89KB -
offchain-messages/dist/index.node.mjs
4.89KB -
transactions/dist/index.browser.mjs
4.07KB -
transactions/dist/index.native.mjs
4.07KB -
transactions/dist/index.node.mjs
4.07KB -
codecs-core/dist/index.browser.mjs
3.62KB -
codecs-core/dist/index.native.mjs
3.62KB -
codecs-core/dist/index.node.mjs
3.62KB -
webcrypto-ed25519-polyfill/dist/index.node.mj
s
3.61KB -
webcrypto-ed25519-polyfill/dist/index.browser
.mjs
3.59KB -
webcrypto-ed25519-polyfill/dist/index.native.
mjs
3.57KB -
rpc-subscriptions/dist/index.browser.mjs
3.37KB -
rpc-subscriptions/dist/index.node.mjs
3.34KB -
rpc-subscriptions/dist/index.native.mjs
3.31KB -
signers/dist/index.browser.mjs
3.26KB -
signers/dist/index.native.mjs
3.26KB -
signers/dist/index.node.mjs
3.26KB -
rpc-transformers/dist/index.browser.mjs
3.16KB -
rpc-transformers/dist/index.native.mjs
3.16KB -
rpc-transformers/dist/index.node.mjs
3.16KB -
keys/dist/index.node.mjs
3.06KB -
addresses/dist/index.browser.mjs
2.93KB -
addresses/dist/index.native.mjs
2.92KB -
addresses/dist/index.node.mjs
2.92KB -
keys/dist/index.browser.mjs
2.85KB -
keys/dist/index.native.mjs
2.85KB -
codecs-strings/dist/index.browser.mjs
2.55KB -
codecs-strings/dist/index.node.mjs
2.51KB -
codecs-strings/dist/index.native.mjs
2.47KB -
transaction-confirmation/dist/index.node.mjs
2.42KB -
transaction-confirmation/dist/index.native.mj
s
2.37KB -
sysvars/dist/index.browser.mjs
2.37KB -
sysvars/dist/index.native.mjs
2.37KB -
transaction-confirmation/dist/index.browser.m
js
2.37KB -
sysvars/dist/index.node.mjs
2.37KB -
rpc/dist/index.node.mjs
1.95KB -
codecs-numbers/dist/index.browser.mjs
1.95KB -
codecs-numbers/dist/index.native.mjs
1.95KB -
codecs-numbers/dist/index.node.mjs
1.94KB -
rpc-transport-http/dist/index.browser.mjs
1.89KB -
rpc-transport-http/dist/index.native.mjs
1.89KB -
rpc/dist/index.native.mjs
1.81KB -
rpc/dist/index.browser.mjs
1.8KB -
rpc-transport-http/dist/index.node.mjs
1.71KB -
rpc-subscriptions-channel-websocket/dist/inde
x.node.mjs
1.33KB -
rpc-subscriptions-channel-websocket/dist/inde
x.native.mjs
1.27KB -
rpc-subscriptions-channel-websocket/dist/inde
x.browser.mjs
1.26KB -
program-client-core/dist/index.browser.mjs
1.21KB -
program-client-core/dist/index.native.mjs
1.21KB -
program-client-core/dist/index.node.mjs
1.21KB -
options/dist/index.browser.mjs
1.18KB -
options/dist/index.native.mjs
1.18KB -
options/dist/index.node.mjs
1.17KB -
accounts/dist/index.browser.mjs
1.17KB -
accounts/dist/index.native.mjs
1.17KB -
accounts/dist/index.node.mjs
1.16KB -
rpc-api/dist/index.browser.mjs
998B -
rpc-api/dist/index.native.mjs
997B -
rpc-api/dist/index.node.mjs
995B -
compat/dist/index.browser.mjs
969B -
compat/dist/index.native.mjs
968B -
compat/dist/index.node.mjs
966B -
rpc-spec-types/dist/index.browser.mjs
962B -
rpc-spec-types/dist/index.native.mjs
961B -
rpc-spec-types/dist/index.node.mjs
959B -
rpc-subscriptions-api/dist/index.native.mjs
871B -
rpc-subscriptions-api/dist/index.browser.mjs
870B -
rpc-subscriptions-api/dist/index.node.mjs
870B -
promises/dist/index.native.mjs
841B -
promises/dist/index.node.mjs
840B -
promises/dist/index.browser.mjs
839B -
plugin-core/dist/index.browser.mjs
820B -
plugin-core/dist/index.native.mjs
819B -
plugin-core/dist/index.node.mjs
817B -
assertions/dist/index.browser.mjs
783B -
instructions/dist/index.browser.mjs
771B -
instructions/dist/index.native.mjs
770B -
instructions/dist/index.node.mjs
768B -
fast-stable-stringify/dist/index.browser.mjs
726B -
fast-stable-stringify/dist/index.native.mjs
725B -
assertions/dist/index.native.mjs
724B -
fast-stable-stringify/dist/index.node.mjs
724B -
assertions/dist/index.node.mjs
723B -
programs/dist/index.browser.mjs
329B -
programs/dist/index.native.mjs
327B -
programs/dist/index.node.mjs
325B -
fs-impl/dist/index.browser.mjs
245B -
event-target-impl/dist/index.node.mjs
230B -
functional/dist/index.browser.mjs
154B -
functional/dist/index.native.mjs
152B -
text-encoding-impl/dist/index.native.mjs
152B -
functional/dist/index.node.mjs
151B -
codecs/dist/index.browser.mjs
145B -
codecs/dist/index.native.mjs
144B -
codecs/dist/index.node.mjs
142B -
event-target-impl/dist/index.browser.mjs
133B -
ws-impl/dist/index.node.mjs
131B -
text-encoding-impl/dist/index.browser.mjs
122B -
fs-impl/dist/index.node.mjs
120B -
text-encoding-impl/dist/index.node.mjs
119B -
ws-impl/dist/index.browser.mjs
113B -
crypto-impl/dist/index.node.mjs
111B -
crypto-impl/dist/index.browser.mjs
109B -
rpc-parsed-types/dist/index.browser.mjs
66B -
rpc-parsed-types/dist/index.native.mjs
65B -
rpc-parsed-types/dist/index.node.mjs
63B -

Total files change +9.01KB +1.71%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Documentation Preview: https://kit-docs-4t3ristse-anza-tech.vercel.app

@mcintyre94 mcintyre94 force-pushed the react/use-tracked-data-swr branch from 948f9b8 to 3457bd3 Compare June 5, 2026 15:13
@mcintyre94 mcintyre94 force-pushed the react/use-subscription-swr branch from f3f39e1 to 7f08fc4 Compare June 5, 2026 15:14
@mcintyre94 mcintyre94 force-pushed the react/use-tracked-data-swr branch from 3457bd3 to b7e063e Compare June 5, 2026 15:31
@mcintyre94 mcintyre94 force-pushed the react/use-subscription-swr branch from 7f08fc4 to 948efe9 Compare June 5, 2026 15:39
@mcintyre94 mcintyre94 force-pushed the react/use-tracked-data-swr branch from b7e063e to 3a03ef4 Compare June 5, 2026 15:39
@mcintyre94 mcintyre94 force-pushed the react/use-subscription-swr branch from 948efe9 to 603b8f5 Compare June 5, 2026 15:47
@mcintyre94 mcintyre94 force-pushed the react/use-tracked-data-swr branch from 3a03ef4 to 24923d0 Compare June 5, 2026 15:47
@mcintyre94 mcintyre94 force-pushed the react/use-subscription-swr branch from 603b8f5 to 783f65e Compare June 5, 2026 15:56
@mcintyre94 mcintyre94 force-pushed the react/use-tracked-data-swr branch from 24923d0 to 1eca8d4 Compare June 5, 2026 15:56
@mcintyre94

Copy link
Copy Markdown
Member Author

@trevor-cortex

@trevor-cortex trevor-cortex left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — would approve if I had the rights to; submitting as a comment review instead.

Adds useTrackedDataSWR — the SWR-backed counterpart to useTrackedData. Wraps createReactiveStoreWithInitialValueAndSlotTracking in useSWRSubscription, bridges getUnifiedState() into next(err, data), ref-syncs the spec and getAbortSignal factory, and uses the established either-null disable pattern (spec == null ? null : key). Ships a thorough browser test suite (loaded/error from both sources, key/spec null transitions, key-change teardown, shared key, abort-signal forwarding, stale-slot dedup, error-after-load retention, stable-key-with-changing-spec, SSR), a typetest, README docs, and a minor changeset.

Structurally a near-mirror of useSubscriptionSWR — same ref-sync, same subscribe-with-empty-deps, same either-null gate, same SSR safety. The only meaningful divergence is the store construction (createReactiveStoreWithInitialValueAndSlotTracking(spec) instead of source.reactiveStore()), which is exactly what the PR description claims. Generics, JSDoc structure, and option merging (SWRConfiguration & UseTrackedDataOptions) all line up with the existing adapters.

Things to watch out for / verify

  • TError defaults to unknown here (matching useSubscriptionSWR), while useRequestSWR defaults to Error. Pre-existing inconsistency, not introduced by this PR — flagging only so subsequent reviewers don't waste cycles on it.
  • Like useSubscriptionSWR, when the spec identity changes but the SWR key stays stable, the new mappers never take effect (the test "keeps the current store when the spec identity changes but the key is stable" pins this behavior). That's the right call for cache-keyed sharing, but it's a sharp edge for users — calling out below as a doc nit.
  • Unlike core useTrackedData, there's no refresh() / reconnect(). PR description explicitly calls this out; the README could mention it too for users coming from the core hook (doc nit below).
  • StrictMode tolerance is exercised via the latching mocks (createMockInitialValueSource replays the settled outcome to remount dispatches; createMockStreamSource replays the log to late subscribers). Worth keeping in mind on any future refactor — these mocks are intentionally shaped to survive the dev double-render, and a naive replacement would surface flakes.
  • No real network / RPC integration test — the bridge is fully exercised against mocks. That's consistent with the sibling hooks; just noting for future reviewers.

Comment thread packages/react/src/swr/useTrackedDataSWR.ts
Comment thread packages/react/src/swr/useTrackedDataSWR.ts
@mcintyre94 mcintyre94 force-pushed the react/use-subscription-swr branch from 783f65e to f835130 Compare June 5, 2026 17:48
@mcintyre94 mcintyre94 force-pushed the react/use-tracked-data-swr branch from 1eca8d4 to d346a51 Compare June 5, 2026 17:48
@mcintyre94

Copy link
Copy Markdown
Member Author

TError defaults to unknown here (matching useSubscriptionSWR), while useRequestSWR defaults to Error. Pre-existing inconsistency, not introduced by this PR — flagging only so subsequent reviewers don't waste cycles on it.

Updated useRequestSWR to use unknown, so these now all match

@mcintyre94 mcintyre94 force-pushed the react/use-tracked-data-swr branch from d346a51 to c57c231 Compare June 5, 2026 17:56
@mcintyre94 mcintyre94 force-pushed the react/use-subscription-swr branch from f835130 to 378f178 Compare June 5, 2026 17:56
@mcintyre94 mcintyre94 marked this pull request as ready for review June 5, 2026 17:57
@mcintyre94 mcintyre94 requested a review from lorisleiva June 5, 2026 17:57

@lorisleiva lorisleiva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@mcintyre94 mcintyre94 force-pushed the react/use-subscription-swr branch from 378f178 to 7491dbe Compare June 10, 2026 15:43
@mcintyre94 mcintyre94 force-pushed the react/use-tracked-data-swr branch 2 times, most recently from 6e705cf to f9a0c0c Compare June 10, 2026 16:49
@mcintyre94 mcintyre94 force-pushed the react/use-subscription-swr branch from 7491dbe to de50afe Compare June 10, 2026 16:49
@mcintyre94 mcintyre94 force-pushed the react/use-tracked-data-swr branch from f9a0c0c to 3739f3c Compare June 10, 2026 18:30
@mcintyre94 mcintyre94 added the do-not-close Add this tag to exempt an issue/PR from being closed by the stalebot label Jun 11, 2026
@mcintyre94 mcintyre94 force-pushed the react/use-subscription-swr branch from de50afe to d07c735 Compare June 12, 2026 17:53
@mcintyre94 mcintyre94 force-pushed the react/use-tracked-data-swr branch from 3739f3c to 98e12a4 Compare June 12, 2026 17:53
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()`).
@mcintyre94 mcintyre94 force-pushed the react/use-subscription-swr branch from d07c735 to b1c4037 Compare June 12, 2026 18:28
@mcintyre94 mcintyre94 force-pushed the react/use-tracked-data-swr branch from 98e12a4 to b0480c0 Compare June 12, 2026 18:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-close Add this tag to exempt an issue/PR from being closed by the stalebot

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants