-
Notifications
You must be signed in to change notification settings - Fork 40
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
useQuery
runs twice with skip=true
and ApolloNextAppProvider
#437
Comments
useQuery
runs twice with skip=true
and ApolloNextAppProvider
This is necessary to prevent hydration mismatches and only happens on the first render of the application in the browser after it loaded - we have to first render with the exact data the component was SSRed with on the server, and then rerender with the actual data the client component would have.
Generally, in React, renders are considered cheap and a single additional rerender should not make any difference to the performance of an application. |
That said, there might be a small optimization we can make to prevent or at least reduce reexecutions of your render functions. I'll take a look. |
Could you give the PR build in #440 (comment) a try and report back? |
Interesting. I think technically it is possible to only re-render if the information had changed, though depending on what we are talking about here, it would come with overhead of comparing unknown data. So probably not ideal. However, I think from
That was how it works in the
Technically this is true... and technically we can ignore it, you are correct. But it is a slippery slope. React as a whole is expensive and RSCs have their overhead as well, yet they are often reached for as a way to lower the JS quantity and execution for performance critical sites. So its just a shame if moving to the lower JS execution solution ends up causing more JS execution. Plus this is a "single" render of a majority of the components on the page. For more flavor to the scenario, I am working on an e-commerce site that will be transitioning from the Styled Components are the major culprit as to why it would take 90ms to hydrate a simple page on a high-end MacBook, but just trying to keep that cost to one initial payment rather than two. |
All that being said, I tried this out and it works as expected. Thank you, and I apologize for not attempting to contribute first. I appreciate your help. |
Yeah, that's what I'm doing now in #440 - it's likely not that expensive and it can save an additional reexecution of the render function (it's not really a rerender, React afaik doesn't go through a full rerender cycle here).
Even if you know that the query is currently being called with
We are in streaming SSR here, so the preloaded value could have been transported over, this component is still suspended on the server, and before it finishes on the server, a user interaction already modifies the client's cache contents. Streaming SSR is really complex here because the app is active both on the server and in the browser at the same time and data is rehydrated "chunk by chunk" Generally, also for your use case, this is an important thing to keep in mind: you can't just transport data over once - the app will already start running in the browser before it finishes running on the server, so it's not possible to wait for all data to be transported before starting it in the browser. |
I had not considered that.
In this case I am pretty sure we can know that. The way things are being preloaded in this case is related to middleware and the root layout's RSCs. The preloaded data is a prop passed into a client context provider in the root layout. So it is just serialized with the layout as the initial data for that client component. Things are definitely complex, but the Layout's server components shouldn't normally be executing again, even on route transitions. |
It's not the end of the world if at some point they did re-execute. But in practice and based on server traces, I haven't seen it occurring. They are higher level than can be affected by client-side user interactions. And the first thing to run on a page request. This conversation helped expand how I think of these situations though... thanks for taking the time to share and fill in some gaps. |
Version info
Behavior
useQuery(QUERY, { skip: true })
causes a re-render after mount of any component leveraging it inside of theApolloNextAppProvider
Expected behavior
useQuery(QUERY, { skip: true })
should not cause any additional renders after mount. This matches the behavior it has when running within the standardApolloProvider
.Consequence
Unable to leverage ApolloClients in the NextJS app directory due to the increased cost of running large chunks of React components twice the expected amount.
Minimal reproduction
https://codesandbox.io/p/devbox/elegant-snow-x9h97v
/example
= pages directory route usingApolloProvider
. Renders once to mount./
= app directory route usingApolloNextAppProvider
. Renders a second time after mount.Screen.Recording.2025-02-14.at.1.24.50.PM.mov
The text was updated successfully, but these errors were encountered: