From c81786aa77055f934ee1059bc95b042b3a521053 Mon Sep 17 00:00:00 2001 From: Scott Cooper Date: Tue, 18 Aug 2026 08:19:38 -0700 Subject: [PATCH 1/3] perf(query-core): Skip unused query result tracking QueriesObserver was building tracked results on every update even when there was no combine function to consume them. Skip that work for normal useQueries notifications. Co-Authored-By: Codex --- .changeset/fresh-mice-listen.md | 5 +++++ packages/query-core/src/queriesObserver.ts | 23 ++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 .changeset/fresh-mice-listen.md diff --git a/.changeset/fresh-mice-listen.md b/.changeset/fresh-mice-listen.md new file mode 100644 index 0000000000..db592344ac --- /dev/null +++ b/.changeset/fresh-mice-listen.md @@ -0,0 +1,5 @@ +--- +'@tanstack/query-core': patch +--- + +Skip unused result tracking when notifying `useQueries` listeners without a `combine` function. diff --git a/packages/query-core/src/queriesObserver.ts b/packages/query-core/src/queriesObserver.ts index c537b557ca..cd69d8f886 100644 --- a/packages/query-core/src/queriesObserver.ts +++ b/packages/query-core/src/queriesObserver.ts @@ -310,14 +310,21 @@ export class QueriesObserver< #notify(): void { if (this.hasListeners()) { - const newTracked = this.#trackResult(this.#result, this.#observerMatches) - const shouldSkipCombine = this.#shouldSkipCombine() - const previousResult = this.#combinedResult - const newResult = shouldSkipCombine - ? previousResult - : this.#combineResult(newTracked, this.#options?.combine) - - if (shouldSkipCombine || previousResult !== newResult) { + let shouldNotify = true + const combine = this.#options?.combine + + if (combine) { + const newTracked = this.#trackResult(this.#result, this.#observerMatches) + const shouldSkipCombine = this.#shouldSkipCombine() + const previousResult = this.#combinedResult + const newResult = shouldSkipCombine + ? previousResult + : this.#combineResult(newTracked, combine) + + shouldNotify = shouldSkipCombine || previousResult !== newResult + } + + if (shouldNotify) { notifyManager.batch(() => { this.listeners.forEach((listener) => { listener(this.#result) From 63268c561ee950d81c361e13ed7f38452ea743ae Mon Sep 17 00:00:00 2001 From: Scott Cooper Date: Tue, 18 Aug 2026 08:22:35 -0700 Subject: [PATCH 2/3] ref(query-core): Reuse combine skip check Treat a missing combine function as another reason to skip combining. This keeps the notification flow close to the existing code and only defers result tracking. Co-Authored-By: Codex --- packages/query-core/src/queriesObserver.ts | 27 +++++++++------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/packages/query-core/src/queriesObserver.ts b/packages/query-core/src/queriesObserver.ts index cd69d8f886..bccf6ce0fd 100644 --- a/packages/query-core/src/queriesObserver.ts +++ b/packages/query-core/src/queriesObserver.ts @@ -256,7 +256,7 @@ export class QueriesObserver< #shouldSkipCombine(): boolean { return ( - this.#options?.combine !== undefined && + this.#options?.combine === undefined || this.#observers.some((observer, index) => { return ( observer.options.suspense && this.#result[index]?.data === undefined @@ -310,21 +310,16 @@ export class QueriesObserver< #notify(): void { if (this.hasListeners()) { - let shouldNotify = true - const combine = this.#options?.combine - - if (combine) { - const newTracked = this.#trackResult(this.#result, this.#observerMatches) - const shouldSkipCombine = this.#shouldSkipCombine() - const previousResult = this.#combinedResult - const newResult = shouldSkipCombine - ? previousResult - : this.#combineResult(newTracked, combine) - - shouldNotify = shouldSkipCombine || previousResult !== newResult - } - - if (shouldNotify) { + const shouldSkipCombine = this.#shouldSkipCombine() + const previousResult = this.#combinedResult + const newResult = shouldSkipCombine + ? previousResult + : this.#combineResult( + this.#trackResult(this.#result, this.#observerMatches), + this.#options?.combine, + ) + + if (shouldSkipCombine || previousResult !== newResult) { notifyManager.batch(() => { this.listeners.forEach((listener) => { listener(this.#result) From 8aa8b010fd684c257ec754193c9ef38dc11e8fb8 Mon Sep 17 00:00:00 2001 From: Scott Cooper Date: Tue, 18 Aug 2026 08:40:53 -0700 Subject: [PATCH 3/3] ref(query-core): Match combine presence check Use the same truthiness check as combineResult so null and other falsy runtime values follow the existing no-combine behavior. Co-Authored-By: Codex --- packages/query-core/src/queriesObserver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/query-core/src/queriesObserver.ts b/packages/query-core/src/queriesObserver.ts index bccf6ce0fd..b3fb41991e 100644 --- a/packages/query-core/src/queriesObserver.ts +++ b/packages/query-core/src/queriesObserver.ts @@ -256,7 +256,7 @@ export class QueriesObserver< #shouldSkipCombine(): boolean { return ( - this.#options?.combine === undefined || + !this.#options?.combine || this.#observers.some((observer, index) => { return ( observer.options.suspense && this.#result[index]?.data === undefined