Skip to content

Commit 74f0d6a

Browse files
authored
fix(useQuery): don't retryOnMount when prefetchInRender is used (#8247)
otherwise, queries will not stay in error state, but immediately go into pending + fetching again; this is also shown by the fact that an additional test now failed, because it didn't reset the error boundary correctly
1 parent fdc5c8e commit 74f0d6a

File tree

2 files changed

+68
-17
lines changed

2 files changed

+68
-17
lines changed

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

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
22
import { fireEvent, waitFor } from '@testing-library/react'
33
import * as React from 'react'
44
import { ErrorBoundary } from 'react-error-boundary'
5-
import { keepPreviousData, useQuery } from '..'
5+
import { QueryErrorResetBoundary, keepPreviousData, useQuery } from '..'
66
import { QueryCache } from '../index'
77
import { createQueryClient, queryKey, renderWithClient, sleep } from './utils'
88

@@ -433,22 +433,27 @@ describe('useQuery().promise', () => {
433433

434434
const rendered = renderWithClient(
435435
queryClient,
436-
<ErrorBoundary
437-
fallbackRender={(props) => (
438-
<>
439-
error boundary{' '}
440-
<button
441-
onClick={() => {
442-
props.resetErrorBoundary()
443-
}}
444-
>
445-
resetErrorBoundary
446-
</button>
447-
</>
436+
<QueryErrorResetBoundary>
437+
{({ reset }) => (
438+
<ErrorBoundary
439+
onReset={reset}
440+
fallbackRender={({ resetErrorBoundary }) => (
441+
<div>
442+
<div>error boundary</div>
443+
<button
444+
onClick={() => {
445+
resetErrorBoundary()
446+
}}
447+
>
448+
resetErrorBoundary
449+
</button>
450+
</div>
451+
)}
452+
>
453+
<Page />
454+
</ErrorBoundary>
448455
)}
449-
>
450-
<Page />
451-
</ErrorBoundary>,
456+
</QueryErrorResetBoundary>,
452457
)
453458

454459
await waitFor(() => rendered.getByText('loading..'))
@@ -464,6 +469,48 @@ describe('useQuery().promise', () => {
464469
expect(queryCount).toBe(2)
465470
})
466471

472+
it('should throw error if the promise fails (colocate suspense and promise)', async () => {
473+
const consoleMock = vi
474+
.spyOn(console, 'error')
475+
.mockImplementation(() => undefined)
476+
477+
const key = queryKey()
478+
479+
function MyComponent() {
480+
const query = useQuery({
481+
queryKey: key,
482+
queryFn: async () => {
483+
await sleep(1)
484+
throw new Error('Error test')
485+
},
486+
retry: false,
487+
})
488+
const data = React.use(query.promise)
489+
490+
return <>{data}</>
491+
}
492+
493+
function Page() {
494+
return (
495+
<React.Suspense fallback="loading..">
496+
<MyComponent />
497+
</React.Suspense>
498+
)
499+
}
500+
501+
const rendered = renderWithClient(
502+
queryClient,
503+
<ErrorBoundary fallbackRender={() => <div>error boundary</div>}>
504+
<Page />
505+
</ErrorBoundary>,
506+
)
507+
508+
await waitFor(() => rendered.getByText('loading..'))
509+
await waitFor(() => rendered.getByText('error boundary'))
510+
511+
consoleMock.mockRestore()
512+
})
513+
467514
it('should recreate promise with data changes', async () => {
468515
const key = queryKey()
469516
let suspenseRenderCount = 0

packages/react-query/src/errorBoundaryUtils.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ export const ensurePreventErrorBoundaryRetry = <
2626
>,
2727
errorResetBoundary: QueryErrorResetBoundaryValue,
2828
) => {
29-
if (options.suspense || options.throwOnError) {
29+
if (
30+
options.suspense ||
31+
options.throwOnError ||
32+
options.experimental_prefetchInRender
33+
) {
3034
// Prevent retrying failed query if the error boundary has not been reset yet
3135
if (!errorResetBoundary.isReset()) {
3236
options.retryOnMount = false

0 commit comments

Comments
 (0)