Skip to content

useChat external store on ChatClient (approach 2/3: subscribe/getSnapshot) - #6

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

useChat external store on ChatClient (approach 2/3: subscribe/getSnapshot)#6
tombeckenham wants to merge 1 commit into
feat/chat-client-persistencefrom
experiment/use-chat-client-store

Conversation

@tombeckenham

Copy link
Copy Markdown
Owner

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

Promote the external store onto ChatClient

Approach 1 (#5) kept the store inside the React hook. But the Solid hook re-implements the identical seven-channel wiring with createSignal, and Vue/Svelte do the same. This PR moves it onto ChatClient so every framework binding shares one primitive.

ai-client

  • Export ChatClientSnapshot (immutable projection of the reactive state) and ChatClientStore { subscribe, getSnapshot }.
  • Add a readonly store field to ChatClient, backed by the existing setX mutators — each of setIsLoading / setStatus / setIsSubscribed / setConnectionStatus / setSessionGenerating / setError and the messages handler also calls emitStoreChange(), which rebuilds the cached snapshot and notifies listeners.
  • Named store (not subscribe) to avoid colliding with the existing live-subscription subscribe().
  • The onXChange callbacks stay intact for existing (Solid/Vue/Svelte/persistor) consumers — the store is purely additive.

ai-react

The hook collapses to:

const snapshot = useSyncExternalStore(
  client.store.subscribe, client.store.getSnapshot, client.store.getSnapshot,
)

No onXChange callbacks wired into React at all — only genuine side-effect callbacks remain.

Trade-off vs approach 3 (@tanstack/store)

Zero new dependencies, but the subscribe/getSnapshot plumbing and snapshot caching are hand-written, and there's no built-in selector support (any change re-renders the whole consumer).

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 — CI's test:pr does this via the build dependency.)

🤖 Generated with Claude Code

Promote the external-store shape out of the React hook and onto ChatClient
itself, so every framework binding can share one subscribe/getSnapshot pair
instead of re-wiring seven onXChange callbacks.

ai-client:
- Add ChatClientSnapshot (immutable projection of the reactive state) and
  ChatClientStore { subscribe, getSnapshot }, exported from the package.
- ChatClient gains a `store` field backed by the existing setX mutators: each
  of setIsLoading/setStatus/setIsSubscribed/setConnectionStatus/
  setSessionGenerating/setError and the messages handler now also calls
  emitStoreChange(), which rebuilds the cached snapshot and notifies listeners.
- Named `store` (not `subscribe`) to avoid colliding with the existing
  live-subscription `subscribe()`.

ai-react:
- useChat drops the hook-local store from the previous rewrite and reads
  client.store via useSyncExternalStore. The hook no longer passes any
  onXChange callback — only genuine side-effect callbacks remain.

The onXChange callbacks stay intact for existing (solid/vue/svelte/persistor)
consumers; the store is additive. All 376 ai-client + 128 ai-react tests pass.

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