Skip to content
Merged
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
19 changes: 16 additions & 3 deletions src/core/infiniteQueryBehavior.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,23 @@ export function infiniteQueryBehavior<
pageParam: param,
}

return Promise.resolve()
.then(() => queryFn(queryFnContext))
let cancelFn: undefined | (() => any)
const queryFnResult = queryFn(queryFnContext)
if ((queryFnResult as any).cancel) {
cancelFn = (queryFnResult as any).cancel
}

const promise = Promise.resolve(queryFnResult)
.then(page => {
newPageParams = previous
? [param, ...newPageParams]
: [...newPageParams, param]
return previous ? [page, ...pages] : [...pages, page]
})
if (cancelFn) {
(promise as any).cancel = cancelFn
}
return promise
}

let promise
Expand Down Expand Up @@ -92,7 +101,11 @@ export function infiniteQueryBehavior<
}
}

return promise.then(pages => ({ pages, pageParams: newPageParams }))
const finalPromise = promise.then(pages => ({ pages, pageParams: newPageParams }))
if ((promise as any).cancel) {
(finalPromise as any).cancel = (promise as any).cancel;
}
return finalPromise;
}
},
}
Expand Down
37 changes: 37 additions & 0 deletions src/react/tests/useInfiniteQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
mockConsoleError,
renderWithClient,
setActTimeout,
Blink,
} from './utils'
import {
useInfiniteQuery,
Expand Down Expand Up @@ -1249,4 +1250,40 @@ describe('useInfiniteQuery', () => {

rendered.getByText('Nothing more to load')
})

it('should cancel the query function when there are no more subscriptions', async () => {
const key = queryKey()
let cancelFn: jest.Mock = jest.fn()

const queryFn = () => {
const promise = new Promise<string>((resolve, reject) => {
cancelFn = jest.fn(() => reject('Cancelled'))
sleep(10).then(() => resolve('OK'))
})

;(promise as any).cancel = cancelFn

return promise
}

function Page() {
const state = useInfiniteQuery(key, queryFn)
return (
<div>
<h1>Status: {state.status}</h1>
</div>
)
}

const rendered = renderWithClient(
queryClient,
<Blink duration={5}>
<Page />
</Blink>
)

await waitFor(() => rendered.getByText('off'))

expect(cancelFn).toHaveBeenCalled()
})
})
37 changes: 37 additions & 0 deletions src/react/tests/useQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
sleep,
renderWithClient,
setActTimeout,
Blink,
} from './utils'
import {
useQuery,
Expand Down Expand Up @@ -2815,4 +2816,40 @@ describe('useQuery', () => {
},
])
})

it('should cancel the query function when there are no more subscriptions', async () => {
const key = queryKey()
let cancelFn: jest.Mock = jest.fn()

const queryFn = () => {
const promise = new Promise<string>((resolve, reject) => {
cancelFn = jest.fn(() => reject('Cancelled'))
sleep(10).then(() => resolve('OK'))
})

;(promise as any).cancel = cancelFn

return promise
}

function Page() {
const state = useQuery(key, queryFn)
return (
<div>
<h1>Status: {state.status}</h1>
</div>
)
}

const rendered = renderWithClient(
queryClient,
<Blink duration={5}>
<Page />
</Blink>
)

await waitFor(() => rendered.getByText('off'))

expect(cancelFn).toHaveBeenCalled()
})
})
17 changes: 17 additions & 0 deletions src/react/tests/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,20 @@ export function setActTimeout(fn: () => void, ms?: number) {
* Assert the parameter is of a specific type.
*/
export const expectType = <T,>(_: T): void => undefined

export const Blink: React.FC<{ duration: number }> = ({
duration,
children,
}) => {
const [shouldShow, setShouldShow] = React.useState<boolean>(true)

React.useEffect(() => {
setShouldShow(true)
const timeout = setTimeout(() => setShouldShow(false), duration)
return () => {
clearTimeout(timeout)
}
}, [duration, children])

return shouldShow ? <>{children}</> : <>off</>
}