Skip to content

Commit

Permalink
fix(solid-query): throwOnError now throws to EB where data is accessed (
Browse files Browse the repository at this point in the history
#7273)

* fix(solid-query): throwOnError now throws to EB where data is accessed
  • Loading branch information
ardeora committed Apr 13, 2024
1 parent b6567cb commit 841d9f7
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 24 deletions.
122 changes: 121 additions & 1 deletion packages/solid-query/src/__tests__/createQuery.test.tsx
Expand Up @@ -2407,6 +2407,7 @@ describe('createQuery', () => {

return (
<div>
<h1>{state.data}</h1>
<h1>{state.status}</h1>
<h2>{state.error?.message}</h2>
</div>
Expand All @@ -2426,6 +2427,124 @@ describe('createQuery', () => {
consoleMock.mockRestore()
})

it('should throw error inside the same component if queryFn throws and throwOnError is in use', async () => {
const key = queryKey()

const consoleMock = vi
.spyOn(console, 'error')
.mockImplementation(() => undefined)

function Page() {
const state = createQuery(() => ({
queryKey: key,
queryFn: () => Promise.reject(new Error('Error test')),
retry: false,
throwOnError: true,
}))

return (
<div>
<ErrorBoundary fallback={() => <div>error boundary</div>}>
<h1>{state.data}</h1>
<h1>{state.status}</h1>
<h2>{state.error?.message}</h2>
</ErrorBoundary>
</div>
)
}

const rendered = render(() => (
<QueryClientProvider client={queryClient}>
<Page />
</QueryClientProvider>
))

await waitFor(() => rendered.getByText('error boundary'))

consoleMock.mockRestore()
})

it('should throw error inside the same component if queryFn throws and show the correct error message', async () => {
const key = queryKey()

const consoleMock = vi
.spyOn(console, 'error')
.mockImplementation(() => undefined)

function Page() {
const state = createQuery(() => ({
queryKey: key,
queryFn: () => Promise.reject(new Error('Error test')),
retry: false,
throwOnError: true,
}))

return (
<div>
<ErrorBoundary
fallback={(err) => <div>Fallback error: {err.message}</div>}
>
<h1>{state.data}</h1>
<h1>{state.status}</h1>
<h2>{state.error?.message}</h2>
</ErrorBoundary>
</div>
)
}

const rendered = render(() => (
<QueryClientProvider client={queryClient}>
<Page />
</QueryClientProvider>
))

await waitFor(() => rendered.getByText('Fallback error: Error test'))

consoleMock.mockRestore()
})

it('should show the correct error message on the error property when accessed outside error boundary', async () => {
const key = queryKey()

const consoleMock = vi
.spyOn(console, 'error')
.mockImplementation(() => undefined)

function Page() {
const state = createQuery(() => ({
queryKey: key,
queryFn: () => Promise.reject(new Error('Error test')),
retry: false,
throwOnError: true,
}))

return (
<div>
<h2>Outside error boundary: {state.error?.message}</h2>
<ErrorBoundary
fallback={(err) => <div>Fallback error: {err.message}</div>}
>
<h1>{state.data}</h1>
<h1>{state.status}</h1>
</ErrorBoundary>
</div>
)
}

const rendered = render(() => (
<QueryClientProvider client={queryClient}>
<Page />
</QueryClientProvider>
))

await waitFor(() =>
rendered.getByText('Outside error boundary: Error test'),
)
await waitFor(() => rendered.getByText('Fallback error: Error test'))

consoleMock.mockRestore()
})

it('should update with data if we observe no properties and throwOnError', async () => {
const key = queryKey()

Expand Down Expand Up @@ -2491,7 +2610,7 @@ describe('createQuery', () => {
const key = queryKey()

function Page() {
const state = createQuery<unknown, Error>(() => ({
const state = createQuery(() => ({
queryKey: key,
queryFn: () => Promise.reject(new Error('Remote Error')),
retry: false,
Expand All @@ -2500,6 +2619,7 @@ describe('createQuery', () => {

return (
<div>
<div>{state.data}</div>
<h1>{state.status}</h1>
<h2>{state.error?.message ?? ''}</h2>
</div>
Expand Down
38 changes: 15 additions & 23 deletions packages/solid-query/src/createBaseQuery.ts
Expand Up @@ -227,12 +227,24 @@ export function createBaseQuery<
}
obs.updateResult()

if (
observerResult.isError &&
!observerResult.isFetching &&
!isRestoring() &&
shouldThrowError(obs.options.throwOnError, [
observerResult.error,
obs.getCurrentQuery(),
])
) {
setStateWithReconciliation(observerResult)
return reject(observerResult.error)
}
if (!observerResult.isLoading) {
const query = obs.getCurrentQuery()
resolve(hydratableObserverResult(query, observerResult))
} else {
setStateWithReconciliation(observerResult)
return resolve(hydratableObserverResult(query, observerResult))
}

setStateWithReconciliation(observerResult)
})
},
{
Expand Down Expand Up @@ -335,26 +347,6 @@ export function createBaseQuery<
),
)

createComputed(
on(
() => state.status,
() => {
const obs = observer()
if (
state.isError &&
!state.isFetching &&
!isRestoring() &&
shouldThrowError(obs.options.throwOnError, [
state.error,
obs.getCurrentQuery(),
])
) {
throw state.error
}
},
),
)

const handler = {
get(
target: QueryObserverResult<TData, TError>,
Expand Down

0 comments on commit 841d9f7

Please sign in to comment.