Skip to content

Commit ee22bef

Browse files
authored
Merge pull request #4732 from reduxjs/bugfix/4240-isSuccess
Keep `isSuccess` consistent when refetching after an error
2 parents 4d92026 + 311f31c commit ee22bef

File tree

2 files changed

+94
-2
lines changed

2 files changed

+94
-2
lines changed

packages/toolkit/src/query/react/buildHooks.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -934,12 +934,13 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
934934
!hasData &&
935935
isFetching
936936

937-
// isSuccess = true when data is present.
937+
// isSuccess = true when data is present and we're not refetching after an error.
938938
// That includes cases where the _current_ item is either actively
939939
// fetching or about to fetch due to an uninitialized entry.
940940
const isSuccess =
941941
currentState.isSuccess ||
942-
((isFetching || currentState.isUninitialized) && hasData)
942+
(hasData &&
943+
((isFetching && !lastResult?.isError) || currentState.isUninitialized))
943944

944945
return {
945946
...currentState,

packages/toolkit/src/query/tests/buildHooks.test.tsx

+91
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import type { MockInstance } from 'vitest'
3939
// the refetching behavior of components.
4040
let amount = 0
4141
let nextItemId = 0
42+
let refetchCount = 0
4243

4344
interface Item {
4445
id: number
@@ -87,6 +88,17 @@ const api = createApi({
8788
},
8889
}),
8990
}),
91+
getUserWithRefetchError: build.query<{ name: string }, number>({
92+
queryFn: async (id) => {
93+
refetchCount += 1
94+
95+
if (refetchCount > 1) {
96+
return { error: true } as any
97+
}
98+
99+
return { data: { name: 'Timmy' } }
100+
},
101+
}),
90102
getIncrementedAmount: build.query<{ amount: number }, void>({
91103
query: () => ({
92104
url: '',
@@ -431,6 +443,85 @@ describe('hooks tests', () => {
431443
])
432444
})
433445

446+
test('isSuccess stays consistent if there is an error while refetching', async () => {
447+
type LoadingState = {
448+
id: number
449+
isFetching: boolean
450+
isSuccess: boolean
451+
isError: boolean
452+
}
453+
const loadingHistory: LoadingState[] = []
454+
455+
function Component({ id = 1 }) {
456+
const queryRes = api.endpoints.getUserWithRefetchError.useQuery(id)
457+
const { refetch, data, status } = queryRes
458+
459+
useEffect(() => {
460+
const { isFetching, isSuccess, isError } = queryRes
461+
loadingHistory.push({ id, isFetching, isSuccess, isError })
462+
}, [id, queryRes])
463+
464+
return (
465+
<div>
466+
<button
467+
onClick={() => {
468+
console.log('Refetching')
469+
refetch()
470+
}}
471+
>
472+
refetch
473+
</button>
474+
<div data-testid="name">{data?.name}</div>
475+
<div data-testid="status">{status}</div>
476+
</div>
477+
)
478+
}
479+
480+
render(<Component />, { wrapper: storeRef.wrapper })
481+
482+
await waitFor(() =>
483+
expect(screen.getByTestId('name').textContent).toBe('Timmy'),
484+
)
485+
486+
fireEvent.click(screen.getByText('refetch'))
487+
488+
await waitFor(() =>
489+
expect(screen.getByTestId('status').textContent).toBe('pending'),
490+
)
491+
492+
await waitFor(() =>
493+
expect(screen.getByTestId('status').textContent).toBe('rejected'),
494+
)
495+
496+
fireEvent.click(screen.getByText('refetch'))
497+
498+
await waitFor(() =>
499+
expect(screen.getByTestId('status').textContent).toBe('pending'),
500+
)
501+
502+
await waitFor(() =>
503+
expect(screen.getByTestId('status').textContent).toBe('rejected'),
504+
)
505+
506+
expect(loadingHistory).toEqual([
507+
// Initial renders
508+
{ id: 1, isFetching: true, isSuccess: false, isError: false },
509+
{ id: 1, isFetching: true, isSuccess: false, isError: false },
510+
// Data is returned
511+
{ id: 1, isFetching: false, isSuccess: true, isError: false },
512+
// Started first refetch
513+
{ id: 1, isFetching: true, isSuccess: true, isError: false },
514+
// First refetch errored
515+
{ id: 1, isFetching: false, isSuccess: false, isError: true },
516+
// Started second refetch
517+
// IMPORTANT We expect `isSuccess` to still be false,
518+
// despite having started the refetch again.
519+
{ id: 1, isFetching: true, isSuccess: false, isError: false },
520+
// Second refetch errored
521+
{ id: 1, isFetching: false, isSuccess: false, isError: true },
522+
])
523+
})
524+
434525
test('useQuery hook respects refetchOnMountOrArgChange: true', async () => {
435526
let data, isLoading, isFetching
436527
function User() {

0 commit comments

Comments
 (0)