Skip to content

💥 Replace the concat mutation hook with mutation scopes - #219

Merged
mbret merged 2 commits into
mainfrom
claude/awesome-bell-2a040j
Nov 3, 2026
Merged

mbret merged 2 commits into
mainfrom
claude/awesome-bell-2a040j

Conversation

@mbret

@mbret mbret commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Why

Writes queued with useConcatMutation$ can overtake each other, so an older value can overwrite a newer one.

The hook delays only the subscription. A mutationFn that starts its request when called still sends every request at once.

  • Change: use TanStack Query's scope option, which useMutation$ already passes through.
  • Gain: each mutation in a scope starts only once the previous one has settled.
  • Cost: callers of the hook have to migrate (major release).

What's new

  • 💥 useConcatMutation$ is removed: queue mutations with useMutation$ and scope: { id }

Other changes

  • ✅ Test that useMutation$ calls sharing a scope start in turn, after a failure or a first value
  • 📝 "Run mutations one after another" section on the useMutation$ docs page

Details

Migration

- useConcatMutation$({ mutationKey: ["save"], mutationFn })
+ useMutation$({ mutationKey: ["save"], scope: { id: "save" }, mutationFn })

The result no longer includes null: data is TData | undefined, and mutateAsync resolves TData.

What the hook did

It built each source with mutationFn(variables) when mutate ran, then waited for earlier mutations before subscribing. Three mutateAsync calls with mutationFn: (v) => from(request(v)) had three requests in flight at once. Deferring the call would fix that, but TanStack Query already keeps the same queue.

How scope behaves

  • scope exists in every @tanstack/react-query version the peer range allows.
  • onMutate runs when mutate is called, then the mutation waits its turn, as it did with the hook.
  • A failed mutation doesn't block the next one.
  • A stream settles its mutation on its first value. The next one starts then.

🔍 Self-reviewed with selfcorrect-review
🤖 Made within harness coverage


Generated by Claude Code

…other

TanStack Query queues mutations that share a `scope.id`. The test pins
that `useMutation$` settles each one in time for the next to start: on
an error, and on the stream's first value while the stream stays open.

Claude-Session: https://claude.ai/code/session_01BvSDyJABLGit75HAjpnsqm
…arallel

`useConcatMutation$` called `mutationFn` as soon as `mutate` ran and
only delayed the subscription. A `mutationFn` that starts its work in
the call, such as `(v) => from(save(v))`, sent every request at once.

TanStack Query queues mutations that share a `scope.id`, and
`useMutation$` passes `scope` through. The useMutation$ docs page now
covers it.

BREAKING CHANGE: `useConcatMutation$` is removed. Queue mutations with a
scope instead: `useConcatMutation$({ mutationKey: ["save"], ... })`
becomes `useMutation$({ scope: { id: "save" }, ... })`, whose `data` is
typed `TData` rather than `TData | null`.

Claude-Session: https://claude.ai/code/session_01BvSDyJABLGit75HAjpnsqm
@mbret mbret self-assigned this Nov 3, 2026
@mbret
mbret marked this pull request as ready for review November 3, 2026 10:56
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-03T10:57:44.263298Z 061cf6e Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@mbret
mbret merged commit 0e6563f into main Nov 3, 2026
2 checks passed
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.

2 participants