Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-infinite-query-signal-reset.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/query-core': patch
---

Reset signal consumption for each new infinite-query fetch. A previous fetch that read the signal no longer causes a later fetch that ignores it to be cancelled on unmount.
35 changes: 35 additions & 0 deletions packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,41 @@ describe('InfiniteQueryBehavior', () => {
vi.useRealTimers()
})

it.each(['refetch', 'fetchNextPage'])(
'should not cancel an unconsumed %s because a previous fetch consumed the signal',
async (method) => {
const key = queryKey()
let consumeSignal = true
const observer = new InfiniteQueryObserver(queryClient, {
queryKey: key,
queryFn: (context) => {
if (consumeSignal) {
void context.signal
return Promise.resolve('initial')
}
return sleep(10).then(() => 'fresh')
},
initialPageParam: 0,
getNextPageParam: (_lastPage, _pages, lastPageParam) =>
lastPageParam + 1,
})
const unsubscribe = observer.subscribe(vi.fn())
await vi.advanceTimersByTimeAsync(0)
consumeSignal = false

const result =
method === 'refetch' ? observer.refetch() : observer.fetchNextPage()
unsubscribe()
await vi.advanceTimersByTimeAsync(10)
await result

expect(queryClient.getQueryData(key)).toEqual({
pages: method === 'refetch' ? ['fresh'] : ['initial', 'fresh'],
pageParams: method === 'refetch' ? [0] : [0, 1],
})
},
)

it('should throw an error if the queryFn is not defined', async () => {
const key = queryKey()

Expand Down
2 changes: 2 additions & 0 deletions packages/query-core/src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,8 @@ export class Query<
}

const abortController = new AbortController()
// Behaviors can replace fetchFn, so reset consumption for every new fetch here too.
this.#abortSignalConsumed = false

// Adds an enumerable signal property to the object that
// which sets abortSignalConsumed to true when the signal
Expand Down
43 changes: 42 additions & 1 deletion packages/react-query/src/__tests__/useInfiniteQuery.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { fireEvent, render } from '@testing-library/react'
import { act, fireEvent, render, renderHook } from '@testing-library/react'
import * as React from 'react'
import { createRenderStream } from '@testing-library/react-render-stream'
import { queryKey, sleep } from '@tanstack/query-test-utils'
Expand Down Expand Up @@ -45,6 +45,47 @@ describe('useInfiniteQuery', () => {
vi.useRealTimers()
})

it.each([false, true])(
'should cache an unconsumed refetch after unmount when an earlier fetch consumed the signal (StrictMode: %s)',
async (strict) => {
const key = queryKey()
let consumeSignal = true
const view = renderHook(
() =>
useInfiniteQuery(
{
queryKey: key,
queryFn: (context) => {
if (consumeSignal) {
void context.signal
return Promise.resolve('initial')
}
return sleep(10).then(() => 'fresh')
},
initialPageParam: 0,
getNextPageParam: () => undefined,
},
queryClient,
),
{ wrapper: strict ? React.StrictMode : undefined },
)
await act(() => vi.advanceTimersByTimeAsync(0))
consumeSignal = false
let result: ReturnType<typeof view.result.current.refetch> | undefined
act(() => {
result = view.result.current.refetch()
})
view.unmount()
await vi.advanceTimersByTimeAsync(10)
await result

expect(queryClient.getQueryData(key)).toEqual({
pages: ['fresh'],
pageParams: [0],
})
},
)

it('should return the correct states for a successful query', async () => {
const key = queryKey()
const states: Array<UseInfiniteQueryResult<InfiniteData<number>>> = []
Expand Down