Skip to content

Conversation

@HuijungYoon
Copy link

@HuijungYoon HuijungYoon commented Dec 15, 2025

fix(query-core): prevent reducer from being called twice in streamedQuery #9787

  • Fix bug where reducer was called twice for each chunk (once in setQueryData, once in loop)
  • Store reducer result in variable to avoid duplicate calls
  • Add test case to verify reducer is only called once per chunk

🎯 Changes

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • 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 an issue where streamed data could be applied twice during a refetch using "replace" mode; stream items are now applied once and the displayed data remains stable during and after refetch.
  • Tests

    • Added a test verifying streamed refetch behavior to ensure stream updates are applied a single time and overall data consistency is preserved.

✏️ Tip: You can customize this high-level summary in your review settings.

…uery TanStack#9787

- Fix bug where reducer was called twice for each chunk (once in setQueryData, once in loop)
- Store reducer result in variable to avoid duplicate calls
- Add test case to verify reducer is only called once per chunk
@changeset-bot
Copy link

changeset-bot bot commented Dec 15, 2025

🦋 Changeset detected

Latest commit: ad98335

The changes in this PR will be included in the next version bump.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

Walkthrough

Updated streamedQuery per-chunk handling to precompute the next reduced value, preventing duplicate reducer invocations; added a test that verifies the reducer is not called twice when refetchMode is "replace", and added a changeset entry documenting the fix.

Changes

Cohort / File(s) Change Summary
Streamed Query Test
packages/query-core/src/__tests__/streamedQuery.test.tsx
Added test "should not call reducer twice when refetchMode is replace" that asserts reducer invocation counts and observable data before and after a refetch with replace mode.
Streamed Query Implementation
packages/query-core/src/streamedQuery.ts
Changed per-chunk processing to compute nextResult = reducer(result, chunk), pass nextResult into setQueryData, then assign result = nextResult to avoid calling the reducer twice.
Changeset
.changeset/fair-peaches-deny.md
Added a changeset documenting a bug fix release entry for @tanstack/query-core: "Fix streamedQuery reducer being called twice".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Pay extra attention to:
    • streamedQuery.ts per-chunk path where nextResult is computed and passed into setQueryData.
    • streamedQuery.test.tsx timing (timer advances) and assertions on reducer call counts and observable data.
    • Assumptions about reducer purity or in-place mutation that could be affected by precomputing results.

Possibly related PRs

Suggested reviewers

  • TkDodo
  • manudeli

Poem

🐇 I hopped through streams of ones and twos,
Collected chunks without repeat blues.
nextResult saved a double call,
Now reducers dance just once—no fall.
🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description includes the required sections from the template (Changes, Checklist, Release Impact) with all checkboxes completed and specific details about the fix.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title accurately describes the main bug fix: preventing the reducer from being called twice in streamedQuery, which is the primary change in this PR.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f15b7fc and bc8d7be.

📒 Files selected for processing (2)
  • packages/query-core/src/__tests__/streamedQuery.test.tsx (1 hunks)
  • packages/query-core/src/streamedQuery.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
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.
📚 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__/streamedQuery.test.tsx
🧬 Code graph analysis (1)
packages/query-core/src/__tests__/streamedQuery.test.tsx (1)
packages/query-core/src/streamedQuery.ts (1)
  • streamedQuery (46-97)

Fixed an issue where the reducer was being invoked twice when refetchMode is set to 'replace'.
Added a regression test to verify the fix.

Fixes TanStack#9787
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/query-core/src/__tests__/streamedQuery.test.tsx (1)

501-536: Past review feedback has been addressed correctly.

The test now properly verifies that the reducer is called exactly once per chunk:

  • Line 525 correctly expects [1, 2, 3] after the initial fetch
  • Line 532 correctly expects [1, 2, 3, 1, 2, 3] after refetch
  • The console.log statement has been removed

The test logic is sound and will catch regressions if the double-call bug reappears.

Minor: Extra whitespace in array literal.

Line 525 has trailing whitespace before the closing bracket: [1, 2, 3 ]. Consider removing it for consistency.

Apply this diff:

-    expect(arr).toEqual([1, 2, 3 ])
+    expect(arr).toEqual([1, 2, 3])
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc8d7be and d58b43c.

📒 Files selected for processing (2)
  • packages/query-core/src/__tests__/streamedQuery.test.tsx (1 hunks)
  • packages/query-core/src/streamedQuery.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/query-core/src/streamedQuery.ts
🧰 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__/streamedQuery.test.tsx

@HuijungYoon
Copy link
Author

  • typo correction
  • fix correct test case

@HuijungYoon HuijungYoon reopened this Dec 15, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d58b43c and 7a81504.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • .changeset/fair-peaches-deny.md (1 hunks)
  • packages/query-core/src/__tests__/streamedQuery.test.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-02T17:57:33.184Z
Learnt from: TkDodo
Repo: TanStack/query PR: 9612
File: packages/query-async-storage-persister/src/asyncThrottle.ts:0-0
Timestamp: 2025-09-02T17:57:33.184Z
Learning: When importing from tanstack/query-core in other TanStack Query packages like query-async-storage-persister, a workspace dependency "tanstack/query-core": "workspace:*" needs to be added to the package.json.

Applied to files:

  • .changeset/fair-peaches-deny.md
📚 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__/streamedQuery.test.tsx
🧬 Code graph analysis (1)
packages/query-core/src/__tests__/streamedQuery.test.tsx (1)
packages/query-core/src/streamedQuery.ts (1)
  • streamedQuery (46-97)
🔇 Additional comments (1)
packages/query-core/src/__tests__/streamedQuery.test.tsx (1)

501-536: LGTM! Test correctly validates the bug fix.

The test properly verifies that the reducer is invoked only once per chunk (not twice as the bug was causing). The test also correctly validates the refetchMode: 'replace' behavior where:

  • The reducer is called for each chunk during both initial fetch and refetch
  • During refetch, cache updates are deferred until the stream completes
  • The observable data correctly reflects the replaced stream [1, 2, 3] after refetch

@HuijungYoon HuijungYoon changed the title fix(query-core): prevent reducer from being called twice in streamedQuery #9787 fix(query-core): prevent reducer from being called twice in streamedQuery Dec 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant