Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a76783d

Browse files
committed
fix(query-core): remove focus check from retryer for refetchIntervallnBackground
1 parent 7c464e3 commit a76783d

File tree

4 files changed

+94
-99
lines changed

4 files changed

+94
-99
lines changed

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

Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ describe('query', () => {
5858
expect(query.gcTime).toBe(200)
5959
})
6060

61-
it('should continue retry after focus regain and resolve all promises', async () => {
61+
it('should continue retry and resolve even with focus state changes', async () => {
6262
const key = queryKey()
6363

6464
// make page unfocused
@@ -86,23 +86,16 @@ describe('query', () => {
8686
result = data
8787
})
8888

89-
// Check if we do not have a result
89+
// Check if we do not have a result initially
9090
expect(result).toBeUndefined()
9191

92-
// Check if the query is really paused
93-
await vi.advanceTimersByTimeAsync(50)
94-
expect(result).toBeUndefined()
92+
// With new behavior, retries continue in background
93+
// Wait for retries to complete
94+
await vi.advanceTimersByTimeAsync(10)
95+
expect(result).toBe('data3')
96+
expect(count).toBe(3)
9597

96-
// Reset visibilityState to original value
9798
visibilityMock.mockRestore()
98-
window.dispatchEvent(new Event('visibilitychange'))
99-
100-
// There should not be a result yet
101-
expect(result).toBeUndefined()
102-
103-
// By now we should have a value
104-
await vi.advanceTimersByTimeAsync(50)
105-
expect(result).toBe('data3')
10699
})
107100

108101
it('should continue retry after reconnect and resolve all promises', async () => {
@@ -153,11 +146,39 @@ describe('query', () => {
153146
onlineMock.mockRestore()
154147
})
155148

156-
it('should throw a CancelledError when a paused query is cancelled', async () => {
149+
it('should continue retry in background when page is not focused', async () => {
157150
const key = queryKey()
158-
159151
// make page unfocused
160152
const visibilityMock = mockVisibilityState('hidden')
153+
let count = 0
154+
let result
155+
const promise = queryClient.fetchQuery({
156+
queryKey: key,
157+
queryFn: () => {
158+
count++
159+
if (count === 3) {
160+
return `data${count}`
161+
}
162+
throw new Error(`error${count}`)
163+
},
164+
retry: 3,
165+
retryDelay: 1,
166+
})
167+
promise.then((data) => {
168+
result = data
169+
})
170+
// Check if we do not have a result initially
171+
expect(result).toBeUndefined()
172+
// Unlike the old behavior, retry should continue in background
173+
// Wait for retries to complete
174+
await vi.advanceTimersByTimeAsync(10)
175+
expect(result).toBe('data3')
176+
expect(count).toBe(3)
177+
visibilityMock.mockRestore()
178+
})
179+
180+
it('should throw a CancelledError when a retrying query is cancelled', async () => {
181+
const key = queryKey()
161182

162183
let count = 0
163184
let result: unknown
@@ -169,7 +190,7 @@ describe('query', () => {
169190
throw new Error(`error${count}`)
170191
},
171192
retry: 3,
172-
retryDelay: 1,
193+
retryDelay: 100,// Longer delay to allow cancellation
173194
})
174195

175196
promise.catch((data) => {
@@ -178,22 +199,20 @@ describe('query', () => {
178199

179200
const query = queryCache.find({ queryKey: key })!
180201

181-
// Check if the query is really paused
182-
await vi.advanceTimersByTimeAsync(50)
202+
// Wait briefly for first failure and start of retry
203+
await vi.advanceTimersByTimeAsync(1)
183204
expect(result).toBeUndefined()
184205

185-
// Cancel query
206+
// Cancel query during retry
186207
query.cancel()
187208

188209
// Check if the error is set to the cancelled error
189210
try {
190211
await promise
191212
expect.unreachable()
192213
} catch {
193-
expect(result).toBeInstanceOf(CancelledError)
194-
} finally {
195-
// Reset visibilityState to original value
196-
visibilityMock.mockRestore()
214+
expect(result instanceof CancelledError).toBe(true)
215+
expect(result instanceof Error).toBe(true)
197216
}
198217
})
199218

@@ -236,6 +255,43 @@ describe('query', () => {
236255
expect(queryCache.find({ queryKey: key })?.state.data).toBe('data')
237256
})
238257

258+
it('should continue refetchInterval with retries in background when tab is inactive', async () => {
259+
const key = queryKey()
260+
const visibilityMock = mockVisibilityState('hidden')
261+
262+
let totalRequests = 0
263+
264+
const queryObserver = new QueryObserver(queryClient, {
265+
queryKey: key,
266+
queryFn: () => {
267+
totalRequests++
268+
// Always fail to simulate network offline
269+
throw new Error(`Network error ${totalRequests}`)
270+
},
271+
refetchInterval: 60000,
272+
refetchIntervalInBackground: true,
273+
retry: 3,
274+
retryDelay: 1,
275+
})
276+
277+
queryObserver.subscribe(() => {})
278+
279+
// First interval: t=0 to t=60s (initial query + retries)
280+
await vi.advanceTimersByTimeAsync(60000)
281+
expect(totalRequests).toBe(4)
282+
283+
// Second interval: t=60s to t=120s (refetch + retries)
284+
await vi.advanceTimersByTimeAsync(60000)
285+
expect(totalRequests).toBe(8)
286+
287+
// Third interval: t=120s to t=180s (refetch + retries)
288+
await vi.advanceTimersByTimeAsync(60000)
289+
expect(totalRequests).toBe(12)
290+
291+
queryObserver.destroy()
292+
visibilityMock.mockRestore()
293+
})
294+
239295
test('should provide context to queryFn', () => {
240296
const key = queryKey()
241297

‎packages/query-core/src/retryer.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { focusManager } from './focusManager'
21
import { onlineManager } from './onlineManager'
32
import { pendingThenable } from './thenable'
43
import { isServer, sleep } from './utils'
@@ -100,7 +99,6 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
10099
}
101100

102101
const canContinue = () =>
103-
focusManager.isFocused() &&
104102
(config.networkMode === 'always' || onlineManager.isOnline()) &&
105103
config.canRun()
106104

‎packages/react-query/src/__tests__/useQuery.test.tsx

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3431,7 +3431,7 @@ describe('useQuery', () => {
34313431
})
34323432

34333433
// See https://github.com/tannerlinsley/react-query/issues/160
3434-
it('should continue retry after focus regain', async () => {
3434+
it('should continue retry in background even when page is not focused', async () => {
34353435
const key = queryKey()
34363436

34373437
// make page unfocused
@@ -3462,29 +3462,8 @@ describe('useQuery', () => {
34623462

34633463
const rendered = renderWithClient(queryClient, <Page />)
34643464

3465-
// The query should display the first error result
3466-
await vi.advanceTimersByTimeAsync(0)
3467-
expect(rendered.getByText('failureCount 1')).toBeInTheDocument()
3468-
expect(
3469-
rendered.getByText('failureReason fetching error 1'),
3470-
).toBeInTheDocument()
3471-
expect(rendered.getByText('status pending')).toBeInTheDocument()
3472-
expect(rendered.getByText('error null')).toBeInTheDocument()
3473-
3474-
// Check if the query really paused
3475-
await vi.advanceTimersByTimeAsync(0)
3476-
expect(rendered.getByText('failureCount 1')).toBeInTheDocument()
3477-
expect(
3478-
rendered.getByText('failureReason fetching error 1'),
3479-
).toBeInTheDocument()
3480-
3481-
act(() => {
3482-
// reset visibilityState to original value
3483-
visibilityMock.mockRestore()
3484-
window.dispatchEvent(new Event('visibilitychange'))
3485-
})
3486-
3487-
// Wait for the final result
3465+
// With the new behavior, retries continue in background
3466+
// Wait for all retries to complete
34883467
await vi.advanceTimersByTimeAsync(4)
34893468
expect(rendered.getByText('failureCount 4')).toBeInTheDocument()
34903469
expect(
@@ -3493,12 +3472,10 @@ describe('useQuery', () => {
34933472
expect(rendered.getByText('status error')).toBeInTheDocument()
34943473
expect(rendered.getByText('error fetching error 4')).toBeInTheDocument()
34953474

3496-
// Check if the query really stopped
3497-
await vi.advanceTimersByTimeAsync(10)
3498-
expect(rendered.getByText('failureCount 4')).toBeInTheDocument()
3499-
expect(
3500-
rendered.getByText('failureReason fetching error 4'),
3501-
).toBeInTheDocument()
3475+
// Verify all retries were attempted
3476+
expect(count).toBe(4)
3477+
3478+
visibilityMock.mockRestore()
35023479
})
35033480

35043481
it('should fetch on mount when a query was already created with setQueryData', async () => {

‎packages/solid-query/src/__tests__/useQuery.test.tsx

Lines changed: 7 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3197,8 +3197,7 @@ describe('useQuery', () => {
31973197
expect(queryFn).toHaveBeenCalledTimes(2)
31983198
})
31993199

3200-
// See https://github.com/tannerlinsley/react-query/issues/160
3201-
it('should continue retry after focus regain', async () => {
3200+
it('should continue retry in background even when page is not focused', async () => {
32023201
const key = queryKey()
32033202

32043203
// make page unfocused
@@ -3233,37 +3232,8 @@ describe('useQuery', () => {
32333232
</QueryClientProvider>
32343233
))
32353234

3236-
// The query should display the first error result
3237-
await vi.waitFor(() =>
3238-
expect(rendered.getByText('failureCount 1')).toBeInTheDocument(),
3239-
)
3240-
await vi.waitFor(() =>
3241-
expect(
3242-
rendered.getByText('failureReason fetching error 1'),
3243-
).toBeInTheDocument(),
3244-
)
3245-
await vi.waitFor(() =>
3246-
expect(rendered.getByText('status pending')).toBeInTheDocument(),
3247-
)
3248-
await vi.waitFor(() =>
3249-
expect(rendered.getByText('error null')).toBeInTheDocument(),
3250-
)
3251-
3252-
// Check if the query really paused
3253-
await sleep(10)
3254-
await vi.waitFor(() =>
3255-
expect(rendered.getByText('failureCount 1')).toBeInTheDocument(),
3256-
)
3257-
await vi.waitFor(() =>
3258-
expect(
3259-
rendered.getByText('failureReason fetching error 1'),
3260-
).toBeInTheDocument(),
3261-
)
3262-
3263-
visibilityMock.mockRestore()
3264-
window.dispatchEvent(new Event('visibilitychange'))
3265-
3266-
// Wait for the final result
3235+
// With the new behavior, retries continue in background
3236+
// Wait for all retries to complete
32673237
await vi.waitFor(() =>
32683238
expect(rendered.getByText('failureCount 4')).toBeInTheDocument(),
32693239
)
@@ -3279,16 +3249,10 @@ describe('useQuery', () => {
32793249
expect(rendered.getByText('error fetching error 4')).toBeInTheDocument(),
32803250
)
32813251

3282-
// Check if the query really stopped
3283-
await sleep(10)
3284-
await vi.waitFor(() =>
3285-
expect(rendered.getByText('failureCount 4')).toBeInTheDocument(),
3286-
)
3287-
await vi.waitFor(() =>
3288-
expect(
3289-
rendered.getByText('failureReason fetching error 4'),
3290-
).toBeInTheDocument(),
3291-
)
3252+
// Verify all retries were attempted
3253+
expect(count).toBe(4)
3254+
3255+
visibilityMock.mockRestore()
32923256
})
32933257

32943258
it('should fetch on mount when a query was already created with setQueryData', async () => {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /