fix(query-core): prevent duplicate abort event listeners in infinite queries
π― Changes
Fixed a memory leak in infiniteQueryBehavior.ts where abort event listeners were being registered multiple times when the signal property was accessed repeatedly during pagination. Root Cause: The addSignalProperty getter was registering a new abort event listener on every signal access without checking if a listener was already attached. This pattern differed from query.ts, which correctly handles this scenario. Solution:
- Added listenerAttached flag to prevent duplicate listener registration (line 25, 32-33)
- Added { once: true } option for automatic listener cleanup (line 39)
- Added test case to verify no duplicate listeners when signal is accessed multiple times
Files Changed:
- packages/query-core/src/infiniteQueryBehavior.ts - Fixed duplicate listener bug
- packages/query-core/src/tests/infiniteQueryBehavior.test.tsx - Added regression test
Impact:
- Prevents memory leaks in infinite queries with many pages
- No API changes, fully backward compatible
- Test coverage: 100% statements, 100% branches for infiniteQueryBehavior.ts
β Checklist
- [O] I have followed the steps in the Contributing guide.
- [O] I have tested this code locally with
pnpm run test:pr.
π Release Impact
- [O] This change affects published code, and I have generated a changeset.
- [ ] This change is docs/CI/dev-only (no release).
Summary by CodeRabbit
-
Bug Fixes
- Fixed a memory leak in infinite queries by ensuring abort listeners are not registered more than once, improving stability and memory usage during paginated loads.
-
Tests
- Added tests confirming abort listeners are attached only once even when the abort signal is accessed repeatedly during query execution.
-
Chores
- Added a changeset entry to publish the patch.
βοΈ Tip: You can customize this high-level summary in your review settings.
π¦ Changeset detected
Latest commit: 26cc89332244e9ca66c012b4e5b6ae52c2d9a13c
The changes in this PR will be included in the next version bump.
This PR includes changesets to release 19 packages
| Name | Type |
|---|---|
| @tanstack/query-core | Patch |
| @tanstack/angular-query-experimental | Patch |
| @tanstack/query-async-storage-persister | Patch |
| @tanstack/query-broadcast-client-experimental | Patch |
| @tanstack/query-persist-client-core | Patch |
| @tanstack/query-sync-storage-persister | Patch |
| @tanstack/react-query | Patch |
| @tanstack/solid-query | Patch |
| @tanstack/svelte-query | Patch |
| @tanstack/vue-query | Patch |
| @tanstack/angular-query-persist-client | Patch |
| @tanstack/react-query-persist-client | Patch |
| @tanstack/solid-query-persist-client | Patch |
| @tanstack/svelte-query-persist-client | Patch |
| @tanstack/react-query-devtools | Patch |
| @tanstack/react-query-next-experimental | Patch |
| @tanstack/solid-query-devtools | Patch |
| @tanstack/svelte-query-devtools | Patch |
| @tanstack/vue-query-devtools | Patch |
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
Walkthrough
Patches @tanstack/query-core to prevent duplicate abort event listeners in infinite queries by ensuring the listener is attached at most once, and adds tests and a changeset documenting the fix.
Changes
| Cohort / File(s) | Summary |
|---|---|
Changeset /.changeset/calm-goats-punch.md |
Adds a patch changeset entry documenting the memory-leak fix for infinite query abort listener handling and a patch version bump. |
Tests packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx |
Adds a test verifying that AbortSignal.prototype.addEventListener is not called multiple times when context.signal is accessed repeatedly; test asserts one listener per page across multiple pages. The diff contains a duplicated test block. |
Implementation packages/query-core/src/infiniteQueryBehavior.ts |
Adds a listenerAttached flag and guards listener registration so the abort listener on context.signal is attached at most once (uses { once: true }); abort still marks cancellation. |
Estimated code review effort
π― 3 (Moderate) | β±οΈ ~20 minutes
- Focus areas:
- Verify correct lifecycle/reset of
listenerAttachedacross pages/fetches and across multiple queryFn invocations. - Ensure no edge cases where reattachment is required (e.g., new signals across retries/pages).
- Remove duplicated test block if unintended and confirm test coverage matches intended behavior.
- Verify correct lifecycle/reset of
Suggested reviewers
- TkDodo
Poem
π° I hopped where signals used to grow,
Two ears, then threeβoh no, no, no!
One little flag, a single bind,
Now aborts stop leaking, peace of mind. π₯
Pre-merge checks and finishing touches
β Passed checks (3 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | β Passed | The title clearly and specifically summarizes the main change: fixing duplicate abort event listeners in infinite queries, which directly matches the core issue addressed in the changeset. |
| Description check | β Passed | The description covers all required template sections: detailed explanation of changes and root cause, completed checklist items, and release impact. All key information is provided with good context. |
| Docstring Coverage | β Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
β¨ Finishing touches
- [ ] π Generate docstrings
π§ͺ Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
π Recent review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π₯ Commits
Reviewing files that changed from the base of the PR and between edb2bcc30ae126d29d7035790dad4d4a4dbd4657 and 26cc89332244e9ca66c012b4e5b6ae52c2d9a13c.
π Files selected for processing (1)
-
packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx(1 hunks)
π§° Additional context used
π§ Learnings (1)
π Learning: 2025-11-22T09:06:05.219Z
Learnt from: sukvvon
Repo: TanStack/query PR: 9892
File: packages/solid-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx:331-335
Timestamp: 2025-11-22T09:06:05.219Z
Learning: In TanStack/query test files, when a queryFn contains side effects (e.g., setting flags for test verification), prefer async/await syntax for clarity; when there are no side effects, prefer the .then() pattern for conciseness.
Applied to files:
-
packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx
π Additional comments (1)
packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx (1)
493-544: LGTM! Test correctly validates the memory leak fix.The test properly addresses the previous review concern by installing the spy on
AbortSignal.prototype.addEventListenerbefore the observer is created, ensuring all listener registrations are captured. The assertion correctly validates that despite accessingcontext.signalthree times per page across three pages (9 total accesses), only 3 abort listeners are registeredβone per page as intended by the fix.The test is well-structured with clear comments explaining both the expected behavior with the fix and what would happen without it.
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.
Comment @coderabbitai help to get the list of available commands and usage tips.