Skip to content

fix: refetch should wait for page component to start streaming #1516

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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

rmarscher
Copy link
Collaborator

Starting with a failing test that uses window.location to demonstrate that refetch resolves early before the requested page has started streaming.

Once we figure out how to solve it, this will close #1437

It's using the window.location to test that refetch does
not resolve before the requested page has started
streaming. There might be a more direct way to test that.
Copy link

vercel bot commented Jul 9, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
waku Ready Ready Preview Aug 20, 2025 2:27pm

Copy link

codesandbox-ci bot commented Jul 9, 2025

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Copy link

pkg-pr-new bot commented Jul 9, 2025

Open in StackBlitz

npm i https://pkg.pr.new/wakujs/waku@1516

commit: 6104a64

@rmarscher
Copy link
Collaborator Author

My primary method of testing is to do this:

pnpm i
cd packages/waku
pnpm run compile
cd ../../e2e/fixtures/create-pages
pnpm i
pnpm run dev

Increase the setTimeout in SlowComponent in e2e/fixtures/create-pages/src/components/LongSuspenseLayout.tsx to a few seconds so it's easier to observe.

Open https://localhost:3000/long-suspense/1 and click on a link.

I added temp console logging to the resolved elements promise in the InnerRouter useEffect. elements is the createFromFetch (from react-server-dom-webpack/client) return value.

console.log('Elements promise resolved', {
  elements,
  rscPath,
  routeData,
  isStatic,
  prefetchOnly,
});

I can see that the page element is a React.lazy component.

So... the problem is that functions to fetch a new page or refetch the current page use fetchRsc which resolves quickly -- because the response does come back from the server with elements. But the page element has not loaded yet and hasn't even started streaming.

We need a function in router/client that wraps fetchRsc, finds the requested page element and awaits it's lazy promise before resolving.

@dai-shi
Copy link
Member

dai-shi commented Aug 20, 2025

fetchRsc which resolves quickly

What would be the timing of resolving the elements?

elements.then(() => console.log('elements resolved'))

Isn't it the timing you want? If React.lazy delays even after that, it feels tricky.


(edit) I'm probably missing something because refetch already returns a same-timing promise.

@hi-ogawa
Copy link
Collaborator

I might be missing some context but how does current behavior compare with Next.js? For example in this demo, navigation between two tabs updated urls immediately while showing loading.js fallback https://app-router.vercel.app/loading.

@rmarscher
Copy link
Collaborator Author

I might be missing some context but how does current behavior compare with Next.js? For example in this demo, navigation between two tabs updated urls immediately while showing loading.js fallback https://app-router.vercel.app/loading.

That looks like a case where you want the UI to eagerly show a loader. In my use case, I want to continue showing the existing content - maybe with a loading indicator - until the next page has loaded -- which seems to work except the window.location is updated and page scroll are happening as soon as the response returns from the server.

I haven't read the NextJS code to see how they handle RSC payloads in their router, but I generated a demo and it has the desired behavior that I'm unable to acheive with Waku. The URL is not updated until the page has loaded. https://github.com/rmarscher/nextjs-long-suspense-demo

@rmarscher
Copy link
Collaborator Author

I found fetchServerResponse and some code where it is matching rsc elements to route segments. I didn't see where it's awaiting the page segment yet... but it does seem that the inner router suspends indefinitely after routing... so maybe that doesn't re-render until the page component has loaded somehow.

https://github.com/vercel/next.js/blob/0fdaf93770d66d61b588bc8d06c5ec2ec0e66323/packages/next/src/client/components/layout-router.tsx#L413-L415

@hi-ogawa
Copy link
Collaborator

It's quite hard to follow next.js but I think window.history manipulation needs to be done inside a separate effect while the destination url is kept in its own state, something like HistoryUpdater and AppRouterState https://github.com/vercel/next.js/blob/0fdaf93770d66d61b588bc8d06c5ec2ec0e66323/packages/next/src/client/components/app-router.tsx#L125

This allows window.history manipulation effect to run only after suspending transition has finished (not just when fetch response has received), which naturally matches with the timing new async component page is rendered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Routing completes before new page component has sent anything
3 participants