Skip to content

Commit 58dc1ce

Browse files
authored
fix(useQuery): fix tracked queries and error boundaries (TanStack#2993)
if you useErrorBoundary, you're likely not observing the error, because you're not using the returned error field - the ErrorBoundary handles the error for you. So when the actual error then happens, we don't inform the observer about it, because we're only informing them if a prop has changed that they are interested in, resulting in the error boundary never being shown. The solution would be to always track the error property if you've set useErrorBoundary
1 parent c2727c1 commit 58dc1ce

File tree

3 files changed

+81
-7
lines changed

3 files changed

+81
-7
lines changed

src/core/queryObserver.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -232,24 +232,38 @@ export class QueryObserver<
232232
}
233233

234234
trackResult(
235-
result: QueryObserverResult<TData, TError>
235+
result: QueryObserverResult<TData, TError>,
236+
defaultedOptions: QueryObserverOptions<
237+
TQueryFnData,
238+
TError,
239+
TData,
240+
TQueryData,
241+
TQueryKey
242+
>
236243
): QueryObserverResult<TData, TError> {
237244
const trackedResult = {} as QueryObserverResult<TData, TError>
238245

246+
const trackProp = (key: keyof QueryObserverResult) => {
247+
if (!this.trackedProps.includes(key)) {
248+
this.trackedProps.push(key)
249+
}
250+
}
251+
239252
Object.keys(result).forEach(key => {
240253
Object.defineProperty(trackedResult, key, {
241254
configurable: false,
242255
enumerable: true,
243256
get: () => {
244-
const typedKey = key as keyof QueryObserverResult
245-
if (!this.trackedProps.includes(typedKey)) {
246-
this.trackedProps.push(typedKey)
247-
}
248-
return result[typedKey]
257+
trackProp(key as keyof QueryObserverResult)
258+
return result[key as keyof QueryObserverResult]
249259
},
250260
})
251261
})
252262

263+
if (defaultedOptions.useErrorBoundary || defaultedOptions.suspense) {
264+
trackProp('error')
265+
}
266+
253267
return trackedResult
254268
}
255269

src/react/tests/QueryResetErrorBoundary.test.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,4 +592,64 @@ describe('QueryErrorResetBoundary', () => {
592592

593593
expect(rendered.queryByText('page')).not.toBeNull()
594594
})
595+
596+
it('should show error boundary when using tracked queries even though we do not track the error field', async () => {
597+
const key = queryKey()
598+
599+
let succeed = false
600+
const consoleMock = mockConsoleError()
601+
602+
function Page() {
603+
const { data } = useQuery(
604+
key,
605+
async () => {
606+
await sleep(10)
607+
if (!succeed) {
608+
throw new Error('Error')
609+
} else {
610+
return 'data'
611+
}
612+
},
613+
{
614+
retry: false,
615+
useErrorBoundary: true,
616+
notifyOnChangeProps: 'tracked',
617+
}
618+
)
619+
return <div>{data}</div>
620+
}
621+
622+
const rendered = renderWithClient(
623+
queryClient,
624+
<QueryErrorResetBoundary>
625+
{({ reset }) => (
626+
<ErrorBoundary
627+
onReset={reset}
628+
fallbackRender={({ resetErrorBoundary }) => (
629+
<div>
630+
<div>error boundary</div>
631+
<button
632+
onClick={() => {
633+
resetErrorBoundary()
634+
}}
635+
>
636+
retry
637+
</button>
638+
</div>
639+
)}
640+
>
641+
<Page />
642+
</ErrorBoundary>
643+
)}
644+
</QueryErrorResetBoundary>
645+
)
646+
647+
await waitFor(() => rendered.getByText('error boundary'))
648+
await waitFor(() => rendered.getByText('retry'))
649+
succeed = true
650+
fireEvent.click(rendered.getByText('retry'))
651+
await waitFor(() => rendered.getByText('data'))
652+
653+
consoleMock.mockRestore()
654+
})
595655
})

src/react/useBaseQuery.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ export function useBaseQuery<
144144

145145
// Handle result property usage tracking
146146
if (defaultedOptions.notifyOnChangeProps === 'tracked') {
147-
result = observer.trackResult(result)
147+
result = observer.trackResult(result, defaultedOptions)
148148
}
149149

150150
return result

0 commit comments

Comments
 (0)