Skip to content

Commit eb559a6

Browse files
ahmethosTkDodo
andauthored
fix(streamedQuery): fall back to initialValue when stream yields no values (#9876)
* fix(streamedQuery): fall back to initialValue when stream yields no values Add failing test for empty streams and update implementation to avoid returning undefined. Includes changeset for patch release. * chore: address review nitpicks (JSDoc + test cleanup) * docs: note that initialValue is returned when the stream yields no values --------- Co-authored-by: Dominik Dorfmeister <office@dorfmeister.cc>
1 parent 8e2e174 commit eb559a6

File tree

4 files changed

+37
-3
lines changed

4 files changed

+37
-3
lines changed

.changeset/puny-walls-eat.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 to avoid returning undefined when the stream yields no values

docs/reference/streamedQuery.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ const query = queryOptions({
4040
- If `TData` is not an array, you must provide a custom `reducer`.
4141
- `initialValue?: TData = TQueryFnData`
4242
- Optional
43-
- Defines the initial data to be used while the first chunk is being fetched.
43+
- Defines the initial data to be used while the first chunk is being fetched, and it is also returned when the stream yields no values.
4444
- It is mandatory when custom `reducer` is provided.
4545
- Defaults to an empty array.

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,35 @@ describe('streamedQuery', () => {
128128
unsubscribe()
129129
})
130130

131+
test('should handle empty streams', async () => {
132+
const key = queryKey()
133+
134+
const observer = new QueryObserver(queryClient, {
135+
queryKey: key,
136+
queryFn: streamedQuery({
137+
streamFn: async function* () {},
138+
}),
139+
})
140+
141+
const unsubscribe = observer.subscribe(vi.fn())
142+
143+
expect(observer.getCurrentResult()).toMatchObject({
144+
status: 'pending',
145+
fetchStatus: 'fetching',
146+
data: undefined,
147+
})
148+
149+
await vi.advanceTimersByTimeAsync(50)
150+
151+
expect(observer.getCurrentResult()).toMatchObject({
152+
status: 'success',
153+
fetchStatus: 'idle',
154+
data: [],
155+
})
156+
157+
unsubscribe()
158+
})
159+
131160
test('should replace on refetch', async () => {
132161
const key = queryKey()
133162
const observer = new QueryObserver(queryClient, {

packages/query-core/src/streamedQuery.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type StreamedQueryParams<TQueryFnData, TData, TQueryKey extends QueryKey> =
4141
* Set to `'replace'` to write all data to the cache once the stream ends.
4242
* @param reducer - A function to reduce the streamed chunks into the final data.
4343
* Defaults to a function that appends chunks to the end of the array.
44-
* @param initialValue - Initial value to be used while the first chunk is being fetched.
44+
* @param initialValue - Initial value to be used while the first chunk is being fetched, and returned if the stream yields no values.
4545
*/
4646
export function streamedQuery<
4747
TQueryFnData = unknown,
@@ -94,6 +94,6 @@ export function streamedQuery<
9494
context.client.setQueryData<TData>(context.queryKey, result)
9595
}
9696

97-
return context.client.getQueryData(context.queryKey)!
97+
return context.client.getQueryData(context.queryKey) ?? initialValue
9898
}
9999
}

0 commit comments

Comments
 (0)