hydrogen icon indicating copy to clipboard operation
hydrogen copied to clipboard

WIP: RSC sub route

Open wizardlyhel opened this issue 3 years ago • 6 comments

Description

RSC Sub routes - Allow Hydrogen to define sub routes with RSC:

Expected behaviour:

  • SSR generates content on server-side as a Suspense fallback
  • Client side hydrates with a __rsc call (for now)

Before submitting the PR, please make sure you do the following:

  • [ ] Read the Contributing Guidelines
  • [ ] Provide a description in this PR that addresses what the PR is solving, or reference the issue that it solves (e.g. fixes #123)
  • [ ] Update docs in this repository according to your change
  • [ ] Run yarn changeset add if this PR cause a version bump based on Keep a Changelog and adheres to Semantic Versioning

wizardlyhel avatar Jul 15 '22 22:07 wizardlyhel

@frandiox This branch is demonstrating how I think we can do an opted-in RSC sub route implementation without making it a major breaking change.

To play with this, just uncomment out <RSCSubRoute /> in the demo-store index.server.tsx. You will see it is trying to make an rsc call to /test path (which I haven't figure out how I can dynamically register a hydrogen route yet)

I notice that we virtually imports hydrogenRoutes in the handleRequest. Isn't this static? Do we need to re-import this on every worker request? or can we actually save this globally in memory?

I am thinking to dynamically register these sub routes as SSR render discovers them .. that is if we can guarantee to have a SSR render before an RSC request.

Another way is to have these sub routes to be also defined in the routes folder so that they also get discovered by the virtual import.

Thoughts?

wizardlyhel avatar Jul 15 '22 23:07 wizardlyhel

@frandiox I updated this PR to demonstrate sub routes working. I think this solution is quite elegant - it doesn't make a major breaking change.

How it works

I put the sub route inside demo store's routes/component folder. Vite picks up the sub route with virtual file routes. Client-side makes the extra rsc call to the sub route.

Thinking about how to get it not make a second __rsc call on full page load

Wondering if I can do a RSC render (along with the fallback <Suspense fallback={cloneElement(page, serverProps)}> and output the rsc output inside in the meta tag with a useID generated id. Then we pass this id along with RSCSubRouteClient so that it can pick up the rsc output that got generated by SSR

wizardlyhel avatar Jul 18 '22 17:07 wizardlyhel

Thanks for putting this together! I haven't had much time yet to play with it but, if I didn't understand it wrong, I think there are a few challenges here:

  • RSC payload in SSR is still in one big chunk so it cannot be reused later when navigating.
  • Even if we are able to split the main RSC payload during SSR in sub-payloads, they still wouldn't make it to the browser cache yet, so perhaps we'd need to use manual in-memory caching before calling fetch for navigation?
  • It seems there's a necessary RSC waterfall request when navigating. I assume this is not a problem for already-cached RSC payloads but it might not be trivial for new navigations. I guess the only way to prevent this is if the browser is aware of what it needs to request beforehand... but that would require some sort of manifest...

Any idea how to deal with any of this? 🤔


A possible approach to explore for the first challenge would be changing RSCSubRoute.server.tsx to do something like this:

  1. rscRenderToReadableStream(actualServerComponent)
  2. .tee()
  3. createFromReadableStream
  4. <>{response.readRoot()}</>
  5. save the other teed RSC stream in request object
  6. render each saved RSC stream to different meta tags during SSR.
  7. somehow, hydrate this in the browser later using all the different inlined sub-RSC payloads❔

I've no idea how this would affect CPU performance though.

frandiox avatar Jul 19 '22 10:07 frandiox

If we send to the client the separate layout maps, then the client should know during transitions which RSC request to make and which not to, so we wouldn't need to rely on browser cache.

blittle avatar Jul 21 '22 22:07 blittle

WIP Sub route API proposal

All new function naming are not final

How to opt-in using Hydrogen sub route

  1. Most likely going to define a new entry-client so that the main page content is also a sub route implementation - I haven't fully hash this out yet

  2. Define sub routes

Global sub routes

Define global sub routes in App.server.tsx and make sure the outlet is included in renderHydrogen. These sub routes will be available on all routes

export const HeaderRSCOutlet = defineRSCOutlet({
  outletName: 'HeaderRSCOutlet',  // haven't figure out how I can avoid the need to have this string to match the export name
  component: Header,
});

function App({request}: HydrogenRouteProps) {
   return (
     <HeaderRSCOutlet />
   );
}

export default renderHydrogen(App, {
  HeaderRSCOutlet,
});

Page sub routes

Define page level sub routes in page routes, for example, routes/index.server.tsx. These are sub routes that will only exist for this page route.

const Test = ({id}: {id: string}) => (<p>Test RSC Outlet {id}</p>)
export const TestRSCOutlet = defineRSCOutlet({
  outletName: 'TestRSCOutlet',
  component: Test,
  dependency: ['id'],
});

export default function Homepage() {
  return (
      <TestRSCOutlet id="123"/>
  );
}

wizardlyhel avatar Aug 10 '22 16:08 wizardlyhel

Define page level sub routes in page routes, for example, routes/index.server.tsx. These are sub routes that will only exist for this page route.

Wouldn't we want some sub-routes to share? Example from remix: image

The global layout would include the left navigation, which is shared across the whole app (like our header & footer), but when on the sales tab, the top navigation is shared for all those "sub routes". How would I describe a tree like this with the proposed API?

blittle avatar Aug 10 '22 17:08 blittle

Oxygen deployed a preview of your kd-correct-customer-api-docs branch. Details:

Storefront Status Preview link Deployment details Last update (UTC)
subscriptions ✅ Successful (Logs) Preview deployment Inspect deployment March 15, 2024 5:18 PM
vite ✅ Successful (Logs) Preview deployment Inspect deployment March 15, 2024 5:18 PM
custom-cart-method ✅ Successful (Logs) Preview deployment Inspect deployment March 15, 2024 5:18 PM
third-party-queries-caching ✅ Successful (Logs) Preview deployment Inspect deployment March 15, 2024 5:18 PM
optimistic-cart-ui ✅ Successful (Logs) Preview deployment Inspect deployment March 15, 2024 5:18 PM
skeleton ✅ Successful (Logs) Preview deployment Inspect deployment March 15, 2024 5:18 PM

Learn more about Hydrogen's GitHub integration.

shopify[bot] avatar Mar 15 '24 14:03 shopify[bot]

Can external headless developers access that shopify slack thread?

coryagami avatar Mar 24 '24 06:03 coryagami