Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(useQuery): fix tracked queries and error boundaries #2993

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 20 additions & 6 deletions src/core/queryObserver.ts
Expand Up @@ -232,24 +232,38 @@ export class QueryObserver<
}

trackResult(
result: QueryObserverResult<TData, TError>
result: QueryObserverResult<TData, TError>,
defaultedOptions: QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey
>
): QueryObserverResult<TData, TError> {
const trackedResult = {} as QueryObserverResult<TData, TError>

const trackProp = (key: keyof QueryObserverResult) => {
if (!this.trackedProps.includes(key)) {
this.trackedProps.push(key)
}
}

Object.keys(result).forEach(key => {
Object.defineProperty(trackedResult, key, {
configurable: false,
enumerable: true,
get: () => {
const typedKey = key as keyof QueryObserverResult
if (!this.trackedProps.includes(typedKey)) {
this.trackedProps.push(typedKey)
}
return result[typedKey]
trackProp(key as keyof QueryObserverResult)
return result[key as keyof QueryObserverResult]
},
})
})

if (defaultedOptions.useErrorBoundary || defaultedOptions.suspense) {
trackProp('error')
}

return trackedResult
}

Expand Down
60 changes: 60 additions & 0 deletions src/react/tests/QueryResetErrorBoundary.test.tsx
Expand Up @@ -592,4 +592,64 @@ describe('QueryErrorResetBoundary', () => {

expect(rendered.queryByText('page')).not.toBeNull()
})

it('should show error boundary when using tracked queries even though we do not track the error field', async () => {
const key = queryKey()

let succeed = false
const consoleMock = mockConsoleError()

function Page() {
const { data } = useQuery(
key,
async () => {
await sleep(10)
if (!succeed) {
throw new Error('Error')
} else {
return 'data'
}
},
{
retry: false,
useErrorBoundary: true,
notifyOnChangeProps: 'tracked',
}
)
return <div>{data}</div>
}

const rendered = renderWithClient(
queryClient,
<QueryErrorResetBoundary>
{({ reset }) => (
<ErrorBoundary
onReset={reset}
fallbackRender={({ resetErrorBoundary }) => (
<div>
<div>error boundary</div>
<button
onClick={() => {
resetErrorBoundary()
}}
>
retry
</button>
</div>
)}
>
<Page />
</ErrorBoundary>
)}
</QueryErrorResetBoundary>
)

await waitFor(() => rendered.getByText('error boundary'))
await waitFor(() => rendered.getByText('retry'))
succeed = true
fireEvent.click(rendered.getByText('retry'))
await waitFor(() => rendered.getByText('data'))

consoleMock.mockRestore()
})
})
2 changes: 1 addition & 1 deletion src/react/useBaseQuery.ts
Expand Up @@ -144,7 +144,7 @@ export function useBaseQuery<

// Handle result property usage tracking
if (defaultedOptions.notifyOnChangeProps === 'tracked') {
result = observer.trackResult(result)
result = observer.trackResult(result, defaultedOptions)
}

return result
Expand Down