Skip to content

fix(query-core): reconnect MutationObserver on resubscribe (StrictMode fix) - #11120

Open
leonardoag2026-beep wants to merge 2 commits into
TanStack:mainfrom
leonardoag2026-beep:fix/mutation-observer-resubscribe-strictmode
Open

fix(query-core): reconnect MutationObserver on resubscribe (StrictMode fix)#11120
leonardoag2026-beep wants to merge 2 commits into
TanStack:mainfrom
leonardoag2026-beep:fix/mutation-observer-resubscribe-strictmode

Conversation

@leonardoag2026-beep

@leonardoag2026-beep leonardoag2026-beep commented Jul 26, 2026

Copy link
Copy Markdown

Look, this is a pr related to this thread #5341.

In the arguments, I saw "that mutations shouldn't have side-effects", "Strict mode tell us we shouldnt use mutate inside use effects", I do understand that "Only on dev happens, but on production it works", I do understand that "useMutationState solves the business" and I also get that "You don't want to waste your time having a workaround to trick StrictMode".

but hey, listen to me. In regards of you not wanting to waste your time, its fine, because this pr solves the business. In regard to using useMutationState, I argue that on a huge codebase, in my opinion it's pretty messy to call useMutationState, because first, isPending should just work regardless of whether you used useEffect or not. isPending should still work reliably on react, without this useMutationState, which in my opinion its only useful for tricking the StrictMode.

I know, that only on dev mode, this issue happens, but listen to me broyos, if i'm developing, i'm gonna test on develop mode, i'm not gonna build to prod everytime I need to make it work.

In regards to "mutations shouldn't have side effects", I argue that Tanstack Query Started as React Query, Tanstack is react's bitch, Bow to it (hey if you didn't laugh I meant no harm, just trying to be funny guy, you dont need to bow if you think the code gets messy).

Now about where the fix lives: onSubscribe() is on query-core, called by Subscribable.subscribe(). It fires for ALL frameworks (React, Solid, Vue, Svelte...). But the scenario that breaks (subscribe → unsubscribe → subscribe on the same tick) is exclusive to React's StrictMode. For every other framework:

  • If #currentMutation is undefined → onSubscribe does nothing
  • If #currentMutation exists → addObserver reconnects (harmless, just adds it back)

So the fix on query-core is safe for everyone. Moving it to react-query wouldn't work because MutationObserver lives on query-core and onSubscribe is a protected method called from inside the class.

But if you still think the change should live only on react-query, I can figure out a way. At least react query would be react's bitch and bow to it, right?


Changes

Adds onSubscribe() to MutationObserver in query-core. When a new listener subscribes and there is an existing #currentMutation, the observer is reconnected to it via addObserver(this), its result is refreshed via #updateResult(), and subscribers are notified via #notify(). This mirrors the existing pattern in QueryObserver.onSubscribe().

Checklist

  • Tests pass (58/58, including mutationObserver, mutations, mutationCache)
  • Appropriate type coverage — no new types added
  • No breaking changes — only adds a hook that's a no-op when #currentMutation is undefined

Release Impact

  • Patch — bug fix, no breaking changes

…e fix)

Add onSubscribe() to MutationObserver to reconnect the observer to an
in-progress mutation when the component resubscribes (e.g. React's
StrictMode double-mount cycle). Without this, the observer is removed
from the mutation's observers list on cleanup and never reconnects,
causing isPending to stay true indefinitely.

QueryObserver already has the same pattern — this makes MutationObserver
consistent with it.
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

MutationObserver.onSubscribe now handles existing mutations by registering the observer, refreshing its current result, and notifying subscribers.

Changes

Mutation observer lifecycle

Layer / File(s) Summary
Wire mutation subscriptions
packages/query-core/src/mutationObserver.ts
onSubscribe registers the observer with the current mutation, updates the current result, and notifies subscribers when a mutation is present.

Estimated code review effort: 2 (Simple) | ~5 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly matches the main change: reconnecting MutationObserver on resubscribe to fix a StrictMode issue.
Description check ✅ Passed The description includes the required Changes, Checklist, and Release Impact sections and explains the fix and its motivation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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