Skip to content

useChat external store on ChatClient (approach 3/3: @tanstack/store) - #7

Draft
tombeckenham wants to merge 1 commit into
feat/chat-client-persistencefrom
experiment/use-chat-tanstack-store
Draft

tombeckenham wants to merge 1 commit into
feat/chat-client-persistencefrom
experiment/use-chat-tanstack-store

Conversation

@tombeckenham

Copy link
Copy Markdown
Owner

Draft / experiment — approach 3 of 3. See #5 for the full writeup and motivation. Base is feat/chat-client-persistence so the diff is only the relevant change.

Back ChatClient's state with @tanstack/store

Same goal as approach 2 (#6) — one external-store primitive on ChatClient, shared across frameworks — but using @tanstack/store instead of a hand-rolled subscribe/getSnapshot.

ai-client

  • Add @tanstack/store dependency.
  • Export ChatClientSnapshot and add readonly store: Store<ChatClientSnapshot> to ChatClient, seeded in the constructor.
  • Each setX mutator and the messages handler calls syncStore(), which projects current state into the Store. The onXChange callbacks stay intact for existing consumers — the Store is additive.

ai-react

  • Add @tanstack/react-store dependency.
  • The hook reads client.store via useStore:
const snapshot = useStore(client.store)
// or scope re-renders per field: useStore(client.store, s => s.messages)

Trade-off vs approach 2 (hand-rolled)

Pros: much less hand-written plumbing, and free selector support (useStore(store, selector)) to scope re-renders per field — useful for token-by-token streaming. It's also the "most TanStack" option.
Cons: two new dependencies (@tanstack/store in ai-client, @tanstack/react-store in ai-react).

Verification

tsc, ESLint (0 errors), all 376 ai-client + 128 ai-react tests pass. (Typecheck requires building ai-client first, since ai-react resolves it via dist types.)

🤖 Generated with Claude Code

Alternative to the hand-rolled subscribe/getSnapshot: hold ChatClient's
reactive state in a @tanstack/store Store and consume it from React with
@tanstack/react-store's useStore.

ai-client:
- Add @tanstack/store dependency.
- Add ChatClientSnapshot (exported) and a `readonly store: Store<ChatClientSnapshot>`
  on ChatClient, seeded in the constructor.
- Each setX mutator (loading/status/subscription/connection/session/error) and
  the messages handler calls syncStore(), which projects current state into the
  Store. The onXChange callbacks stay intact for existing consumers; the Store
  is additive.

ai-react:
- Add @tanstack/react-store dependency.
- useChat reads client.store via useStore. A selector
  (useStore(client.store, s => s.messages)) could scope per-field re-renders;
  kept whole here to match the public API.

vs the subscribe/getSnapshot branch: less hand-written plumbing and free
selector support, at the cost of two new @tanstack/store deps. All 376
ai-client + 128 ai-react tests pass (typecheck requires ai-client to be built
first, as ai-react resolves it via dist types).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

🚀 Changeset Version Preview

6 package(s) bumped directly, 5 bumped as dependents.

🟥 Major bumps

Package Version Reason
@tanstack/ai-elevenlabs 0.2.17 → 1.0.0 Dependent
@tanstack/ai-openai 0.12.0 → 1.0.0 Dependent
@tanstack/ai-react-ui 0.8.5 → 1.0.0 Dependent
@tanstack/ai-solid-ui 0.7.5 → 1.0.0 Dependent

🟨 Minor bumps

Package Version Reason
@tanstack/ai-client 0.15.2 → 0.16.0 Changeset
@tanstack/ai-preact 0.8.2 → 0.9.0 Changeset
@tanstack/ai-react 0.14.2 → 0.15.0 Changeset
@tanstack/ai-solid 0.12.2 → 0.13.0 Changeset
@tanstack/ai-svelte 0.12.2 → 0.13.0 Changeset
@tanstack/ai-vue 0.12.2 → 0.13.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai-vue-ui 0.2.11 → 0.2.12 Dependent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant