Comments (4)
I see what's going on here. The problem is that when a suspense query errors and has no data yet, it throws an error to the error boundary. When that happens, you need to explicitly tell the library when it's fine to re-fetch the query, because react will re-render components even though they already threw the error once more before showing the error boundary. It's an implementation detail of react, but for us, it would mean another refetch attempt.
So there is no refetch happening because the error boundary wasn't reset. In your example, there is no error boundary at all, that's why you see the white page. If there was an error boundary with the recommended integration, you would see the boundary and be able to reset it.
We noticed this while using fetchQuery in a router loader in combination with useSuspenseQuery in the route component.
I'm not quite following here. When a route loader throws an error, it should show the route error boundary. The route component that uses useSuspenseQuery
wouldn't render. But again, even if, throwing "the same error" to the error boundary seems okay.
from query.
Thanks for investigating! If I understand correctly, the case you described seems to be slightly different. The first fetch is not triggered by a suspense query, but rather by a fetchQuery (or prefetchQuery). Therefore the recommended error boundary does not catch the initial error.
The second time the queryFn runs, it does not throw an error. That is why I would expect the suspense query to fetch successfully and the component to render. However the suspense query does not call the queryFn at all and instead instantly returns the error previously encountered in the fetchQuery.
Here is an updated example with an error boundary..
I would expect the component with the suspense query to render in this case:
- Click "Fetch query" (Fetches query and runs into error)
- Click "Show component with query"
Instead the error boundary renders its fallback.
It seems like the initial fetchQuery primes the query error boundary, even so it is not triggered from inside a suspense query. Is that to be expected? If so, is there a sensible way to handle this case?
from query.
The first fetch is not triggered by a suspense query, but rather by a fetchQuery (or prefetchQuery). Therefore the recommended error boundary does not catch the initial error.
why not? you mentioned route loaders? Not sure which router you are using, but both react-router and tanstack-router integrate with error boundaries, so if you throw an error in a route loader (which fetchQuery
does), it should show the error boundary. Not that prefetchQuery
does not throw errors.
Instead the error boundary renders its fallback.
Like I said, I think that's expected. useSuspenseQuery
cannot trigger a new fetch because the error hasn't been reset yet with useQueryErrorResetBoundary
. This happens here:
query/packages/react-query/src/errorBoundaryUtils.ts
Lines 29 to 35 in 639c725
So when fetchQuery
primes a query in error state, useSuspenseQuery
picks up that error, sees that we haven't reset yet, so it re-throws to show the error. Then, when the user resets, it re-fetches when it mounts again.
is there a sensible way to handle this case?
it seems like you are expecting fetches triggered by fetchQuery
to behave differently than fetches triggered from useSuspenseQuery
. That is not the case. The Query itself doesn't care what triggers it.
One way to go about is to catch errors from fetchQuery
and then immediately remove the error'd query with queryClient.removeQueries
. It seems like you are not interested in the error that happens in the route loader. I think the actual flow should be:
- route loader fetches and it errors
- the error boundary is shown
- user retries the error
- route loader runs again (or component renders, doesn't matter)
- fetch succeeds and component is shown
from query.
Not sure which router you are using, but both react-router and tanstack-router integrate with error boundaries, so if you throw an error in a route loader (which fetchQuery does), it should show the error boundary. Not that prefetchQuery does not throw errors.
We are using tanstack-router and use the loader in a similar way to what is described in the tanstack-query docs. Simplified our setup looks like this:
// src/routes/route.tsx
export const Route = createFileRoute('/route')({
component: () => <RouteComponent />,
loader() {
fetchQueryOne();
fetchQueryTwo();
},
});
function RouteComponent() {
const queryOne = useSuspenseQueryOne()
return (
...
<Suspense fallback="loading ...">
<NestedComponent />
</Suspense>
)
}
function NestedComponent() {
const queryTwo = useSuspenseQueryTwo()
return (
...
)
}
// src/routes/__root.tsx
export const Route = createRootRoute({
component: () => {
if (!isAuthenticated()) {
return <Login />
}
return <Outlet />
},
});
We utilize the route loader to avoid waterfall loading of suspense queries but still want to control where to show a suspense fallback deeper inside the component tree. Additionally we don't render the outlet at all, when a user is not authenticated.
I guess the issue with this setup is, that we do not await the queries in the loader and therefore the router error boundary does not get triggered. We also do not render the route component in case of missing authentication, so the suspense queries do not even get triggered.
This results in this behavior:
- auth token runs out (e.g. due to inactivity)
- user returns and loads the page
- route loader runs and triggers the queries
- login component is rendered, as the user does not have a valid auth token
- queries run into auth error, which primes the query error boundary
- user logs in again
- route component triggers the suspense queries
- suspense queries return the error
- router error boundary is shown
- user retries
- route component renders
From a user perspective it seems weird that the error boundary is shown with an auth error, even so she just logged in.
One way to go about is to catch errors from fetchQuery and then immediately remove the error'd query with queryClient.removeQueries.
Thanks for the idea! I was looking for something like queryClient.resetErroBoundary
, but did not see the connection to removeQueries
. If we stick with our current setup this might indeed be the best way to handle it.
Other than that it might be best to rethink authentication handling in our router setup and use the redirect approach instead to avoid the route loader runs for unauthenticated users.
Really appreciate your help and insights!
from query.
Related Issues (20)
- Bug: Error when hydrating an query that threw error HOT 1
- useQuery() hook vulnerable to type errors HOT 1
- [svelte-query] `getQueryData`/`setQueryData` fail to infer query data type from `queryOptions` HOT 3
- ESLint Plugin: Flat Config Support HOT 2
- [react-query][Bug?]When invalidating useSuspenseQuery queries with invalidateQueries, the refetch process does not run. HOT 1
- useIsFetching stops tracking queries immediately HOT 1
- Typescript issue with queryOptions and usePrefetchQuery HOT 2
- No QueryClient set, use QueryClientProvider to set one
- Cannot read properties of null (reading 'useRef') with Vite Module Federation HOT 1
- docs: make the incompatibility of `skipToken` and `refetch` more detailed HOT 1
- "React Example: Nextjs Suspense Streaming" doesn't work HOT 1
- invoking `suspense()` with `enabled: false` will wait infinite while ssr HOT 3
- Vercel build error when using HydrationBoundary HOT 4
- No information about matchQuery in the docs HOT 2
- Hydration Boundary not working with 2 or more children HOT 1
- Query that is not enabled returns the result stored in the cache HOT 5
- preserveSymlinks breaks useQuery return type HOT 1
- React Query v5.48.0 Cannot read properties of undefined (reading 'length') when using infinite queries HOT 3
- Enabled exactOptionalPropertyTypes causes error when trying to use the optional selector HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from query.