Skip to content

Commit d576092

Browse files
HuijungYoonTkDodoautofix-ci[bot]
authored
fix(query-core): prevent reducer from being called twice in streamedQuery (#9970)
* 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 * fix(streamed-query): prevent reducer from being called twice 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 #9787 * docs: add changeset and lockfile update * fix(changeset): change version bump to patch * Update streamedQuery.test.tsx fix: lint * fix: support refetchMode append and prevent double reducer calls - Fix refetchMode append to preserve existing data on refetch - Fix reducer being called twice by calling it once and storing result - Update pnpm-lock.yaml to match main branch * chore: update pnpm-lock.yaml to include size-limit dependencies * ci: apply automated fixes * refactor: simplify streamedQuery logic with clearer isReplaceRefetch condition - Extract isReplaceRefetch variable for better readability - Use explicit if-else instead of negated conditions - Make it clearer when reducer is called in each branch --------- Co-authored-by: TkDodo <[email protected]> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent f9fc56a commit d576092

File tree

3 files changed

+49
-4
lines changed

3 files changed

+49
-4
lines changed

.changeset/fair-peaches-deny.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/query-core': patch
3+
---
4+
5+
Fix streamedQuery reducer being called twice

packages/query-core/src/__tests__/streamedQuery.test.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,4 +538,41 @@ describe('streamedQuery', () => {
538538

539539
unsubscribe()
540540
})
541+
542+
test('should not call reducer twice when refetchMode is replace', async () => {
543+
const key = queryKey()
544+
const arr: Array<number> = []
545+
546+
const observer = new QueryObserver(queryClient, {
547+
queryKey: key,
548+
queryFn: streamedQuery({
549+
streamFn: async function* () {
550+
const v = [1, 2, 3]
551+
yield* v
552+
},
553+
reducer: (oldStream, newChunk) => {
554+
arr.push(newChunk)
555+
return [...oldStream, newChunk]
556+
},
557+
initialValue: [] as Array<number>,
558+
refetchMode: 'replace',
559+
}),
560+
})
561+
562+
const unsubscribe = observer.subscribe(vi.fn())
563+
564+
await vi.advanceTimersByTimeAsync(0)
565+
566+
expect(arr).toEqual([1, 2, 3])
567+
expect(observer.getCurrentResult().data).toEqual([1, 2, 3])
568+
569+
void observer.refetch()
570+
571+
await vi.advanceTimersByTimeAsync(0)
572+
573+
expect(arr).toEqual([1, 2, 3, 1, 2, 3])
574+
expect(observer.getCurrentResult().data).toEqual([1, 2, 3])
575+
576+
unsubscribe()
577+
})
541578
})

packages/query-core/src/streamedQuery.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,22 +95,25 @@ export function streamedQuery<
9595

9696
const stream = await streamFn(streamFnContext)
9797

98+
const isReplaceRefetch = isRefetch && refetchMode === 'replace'
99+
98100
for await (const chunk of stream) {
99101
if (cancelled) {
100102
break
101103
}
102104

103-
// don't append to the cache directly when replace-refetching
104-
if (!isRefetch || refetchMode !== 'replace') {
105+
if (isReplaceRefetch) {
106+
// don't append to the cache directly when replace-refetching
107+
result = reducer(result, chunk)
108+
} else {
105109
context.client.setQueryData<TData>(context.queryKey, (prev) =>
106110
reducer(prev === undefined ? initialValue : prev, chunk),
107111
)
108112
}
109-
result = reducer(result, chunk)
110113
}
111114

112115
// finalize result: replace-refetching needs to write to the cache
113-
if (isRefetch && refetchMode === 'replace' && !cancelled) {
116+
if (isReplaceRefetch && !cancelled) {
114117
context.client.setQueryData<TData>(context.queryKey, result)
115118
}
116119

0 commit comments

Comments
 (0)