-
-
Couldn't load subscription status.
- Fork 10.7k
Feat: Add createStaticHandler for Router SSR support #9010
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’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
🦋 Changeset detectedLatest commit: a037379 The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
| () => router.state, | ||
| // We have to provide this so React@18 doesn't complain during hydration, | ||
| // but we pass our serialized hydration data into the router so state here | ||
| // is already synced with what the server saw |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixes the current hydration bug (#8972)
| navigator={navigator} | ||
| > | ||
| <DataRoutes routes={routes} children={children} /> | ||
| <DataRoutes routes={router.routes} children={children} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure we are always passing through data routes
| // Call action if we received an action submission | ||
| let pendingActionData: RouteData | null = null; | ||
| let pendingActionError: RouteData | null = null; | ||
| let request = createRequest(location, opts?.submission); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the big change to current router internals in here. In order to better align the server/browser use-cases we life Request creation way up from where it used to be. That way we're better able to accept an external Request ion the SSR case
| // Note: OK to mutate this in-place since it's a scoped var inside | ||
| // handleAction and mutation will not impact the startNavigation matches | ||
| // variable that we use for revalidation | ||
| matches = matches.slice(0, -1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is handled by getTargetMatch now
| }); | ||
| } | ||
|
|
||
| if (isRouteRequest) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For Route requests we just return the data here, don't proceed with loaders
| let routeData = [result.errors, result.actionData, result.loaderData].find( | ||
| (v) => v | ||
| ); | ||
| let value = Object.values(routeData || {})[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't ever return state to a queryRoute call, and instead grab the raw data. Should think about the non-Response error flow here. When we pick off the "value" here we sort of lose visibility as to whether it was an error or not. If user's are always throwing Error instances they could check themselves. but what if they throw 'not an error';...?
| result = await handler({ | ||
| params: match.params, | ||
| request: createRequest(location, submission), | ||
| request, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the old request creation spot - way down at the point we call a loader/action
| // without unwrapping | ||
| if (isRouteRequest) { | ||
| throw result; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't unwrap responses for queryRoute
| // redirect | ||
| if (skipRedirects) { | ||
| throw result; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't process redirects for query
|
Closing in favor of a rebased branch on top of the |
Adds a
createStaticHandlermethod for the router usage on the server. This exposes 2 methods:let { dataRoutes, query, queryRoute } = createStaticHandler({ routes })dataRoutesis therouteswith guaranteed unique IDs on them, and is required to be passed to<DataStaticRouter>query(Request) => Promise<StaticRouterState | Response>Responseis returned - it should be handled as an HTTP response<DataStaticRouter dataRoutes={dataRoutes} {state={state}>queryRoute(Request, routeId) => Promise<any>Responseis returned - it should be handled as an HTTP responseThis also fixes a small
DataBrowserRouterhydration error due to a missing param foruseSyncExternalStoreShimReplaces #8986