Skip to content

Commit

Permalink
Merge remote-tracking branch 'react-query/v4' into 2927-offline-queries
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/core/retryer.ts
  • Loading branch information
TkDodo committed Nov 28, 2021
2 parents d8d893f + fb13a05 commit c7bf4aa
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/core/query.ts
Expand Up @@ -361,6 +361,8 @@ export class Query<
// Silently cancel current fetch if the user wants to cancel refetches
this.cancel({ silent: true })
} else if (this.promise) {
// make sure that retries that were potentially cancelled due to unmounts can continue
this.retryer?.continueRetry()
// Return current promise if we are already fetching
return this.promise
}
Expand Down
5 changes: 5 additions & 0 deletions src/core/retryer.ts
Expand Up @@ -60,6 +60,7 @@ export function isCancelledError(value: any): value is CancelledError {
export class Retryer<TData = unknown, TError = unknown> {
cancel: (options?: CancelOptions) => void
cancelRetry: () => void
continueRetry: () => void
continue: () => void
failureCount: number
isPaused: boolean
Expand All @@ -83,6 +84,10 @@ export class Retryer<TData = unknown, TError = unknown> {
cancelRetry = true
}

this.continueRetry = () => {
cancelRetry = false
}

const shouldPause = () =>
!focusManager.isFocused() ||
(config.networkMode !== 'always' && !onlineManager.isOnline())
Expand Down
105 changes: 105 additions & 0 deletions src/reactjs/tests/useQuery.test.tsx
Expand Up @@ -2723,6 +2723,111 @@ describe('useQuery', () => {
consoleMock.mockRestore()
})

it('should continue retries when observers unmount and remount while waiting for a retry (#3031)', async () => {
const key = queryKey()
const consoleMock = mockConsoleError()
let count = 0

function Page() {
const result = useQuery(
key,
async () => {
count++
await sleep(10)
return Promise.reject('some error')
},
{
retry: 2,
retryDelay: 100,
}
)

return (
<div>
<div>error: {result.error ?? 'null'}</div>
<div>failureCount: {result.failureCount}</div>
</div>
)
}

function App() {
const [show, toggle] = React.useReducer(x => !x, true)

return (
<div>
<button onClick={toggle}>{show ? 'hide' : 'show'}</button>
{show && <Page />}
</div>
)
}

const rendered = renderWithClient(queryClient, <App />)

await waitFor(() => rendered.getByText('failureCount: 1'))
rendered.getByRole('button', { name: /hide/i }).click()
rendered.getByRole('button', { name: /show/i }).click()
await waitFor(() => rendered.getByText('error: some error'))

expect(count).toBe(3)

consoleMock.mockRestore()
})

it('should restart when observers unmount and remount while waiting for a retry when query was cancelled in between (#3031)', async () => {
const key = queryKey()
const consoleMock = mockConsoleError()
let count = 0

function Page() {
const result = useQuery(
key,
async () => {
count++
await sleep(10)
return Promise.reject('some error')
},
{
retry: 2,
retryDelay: 100,
}
)

return (
<div>
<div>error: {result.error ?? 'null'}</div>
<div>failureCount: {result.failureCount}</div>
</div>
)
}

function App() {
const [show, toggle] = React.useReducer(x => !x, true)

return (
<div>
<button onClick={toggle}>{show ? 'hide' : 'show'}</button>
<button onClick={() => queryClient.cancelQueries({ queryKey: key })}>
cancel
</button>
{show && <Page />}
</div>
)
}

const rendered = renderWithClient(queryClient, <App />)

await waitFor(() => rendered.getByText('failureCount: 1'))
rendered.getByRole('button', { name: /hide/i }).click()
rendered.getByRole('button', { name: /cancel/i }).click()
rendered.getByRole('button', { name: /show/i }).click()
await waitFor(() => rendered.getByText('error: some error'))

// initial fetch (1), which will be cancelled, followed by new mount(2) + 2 retries = 4
expect(count).toBe(4)

consoleMock.mockRestore()
})

it('should always fetch if refetchOnMount is set to always', async () => {
const key = queryKey()
const states: UseQueryResult<string>[] = []
Expand Down

0 comments on commit c7bf4aa

Please sign in to comment.