Closed schiller-manuel closed 2 months ago
Here is a workaround:
const router = createRouter({
...
});
+ router.buildAndCommitLocation({});
+ // but... why?
I can't remember where else we discussed this, but routeContext
being used in a component is an anti-pattern if you're doing SSR. Let me explain why:
Hydration needs to be relatively synchronous. We can't wait for async assets before rendering
To accomplish this, anything that was async on the server is serialized down to the client and made ready before hydration
Context is not guarantied to be serializable, so we can't serialize it.
This means that on the initial hydration render of the app, the best "context" we could provide is the router context, which should also be isomorphic (e.g. if you supply something like a query client, it should be instantiated on both the server and the client).
We can't make hydration async, and we wouldn't want to either way
We can't make beforeLoad
sync
We can't guaranty serialization of routeContext
, and even if we could mix, we would still need to be able to wait on the async nature of it to build up the routeContext
Proposed solution:
routeContext
in components.Route.useRouteContext
hook.routeContext
in loader
s.This is not SSR related. just an ordinary SPA.
Sorry, you are correct. This should be working for non-SSR renderings. However, the issue stands. What should we do?
Removing the user access to the routeContext
outside of the loader
function would make it difficult for anyone using it to build something like a breadcrumbs component. Also, the route context does allow for patterns where you can in a single location permeate some options that'll be the same in the loader and in the component.
This sort of pattern is pretty handy and IMO it would be a shame to lose.
// src/routes/posts.tsx
import { createFileRoute } from '@tanstack/react-router';
import { useSuspenseQuery, queryOptions } from '@tanstack/react-query';
export const Route = createFileRoute('/posts')({
beforeLoad: ({ params }) => {
return { // only calculate what the queryOptions were once
postOptions: queryOptions({ queryKey: ['posts', params.postId], ... }),
postCommentsOptions: queryOptions({ queryKey: ['posts', params.postId, 'comments'], ... })
}
},
loader: async ({ context }) => {
await Promise.allSettled([ // you can also choose not to await here, just kick off Query
context.queryClient.ensureQueryData(context.postOptions),
context.queryClient.ensureQueryData(context.postCommentsOptions),
])
},
component: Posts
})
function Posts() {
const { postOptions, postCommentOptions } = Route.useRouteContext();
const postQuery = useSuspenseQuery(postOptions);
const postCommentsQuery = useSuspenseQuery(postCommentsOptions);
// ...
}
If I remember correctly, for a period of time during the beta when the loaderData
was dropped, the router was setup in a way to accept non-serializable content (like a queryClient, apiFetcher, etc.), but everything returned by a Route's beforeLoad
function had to be serializable.
👆🏼Would something like this be feasible here?
reproducer in https://github.com/TanStack/router/pull/2045
this is fixed in 1.49.1
Describe the bug
Upon full page reload,
useRouteContext
returnsundefined
.Your Example Website or App
https://stackblitz.com/edit/tanstack-router-qqb8ia?file=src%2Froutes%2Ffoo%2Findex.tsx&preset=node
Steps to Reproduce the Bug or Issue
Expected behavior
route context is never undefined
Screenshots or Videos
https://github.com/user-attachments/assets/d8f0989c-7541-4771-8c98-65e80594df1a
Platform
Additional context
No response