-
Notifications
You must be signed in to change notification settings - Fork 8
Description
Hello. I first want to thank you for this project, this helps enormously with the tech stack I'm looking to use.
However, I have been experiencing a bug in which SSR data never actually gets passed to the client.
The problem is as follows:
For performance reasons, svelte runs the load handlers of +layout.server.ts and +page.server.ts in parallel (with +layout.server.ts receiving the execution context first). For this reason, an escape hatch was created, namely parent for when you require the data of the layout in the page. But an inverse relationship where layout depends on page is not possible. Data is serialized into json immediately after the load function, and once all load functions resolve, these serialized strings are aggregated and sent in the final response.
Nodejs is not truly multithreaded and instead opts for an event loop, meaning that only one function truly gets executed at any given time. +layout.server.ts, being the first of the nodes executed, sets the trpc data variable via hydrateToClient. This in turn creates the empty TRPC_SSR_DATA map to be populated by .ssr() calls, which is returned by the +layout load() hook. Now here's the important part: This empty map is returned and immediately serialized by add_node. After this, execution is passed over to load() from +page.server.ts, where .ssr() is called and TRPC_SSR_DATA is populated. As +page.server.ts does not return anything, none of this data gets serialized. However, during SSR, the data does actually get used for rendering the html as it awaits all load functions to finish. The render html gets received by the client, but with an empty tRPC data map. The client invalidates the data and performs the tRPC query, but while the query is pending, as the data is empty, this empty data is render on the client. Once the query completes this data is shown again. This causes a brief but annoying layout shift during the query network request.
To reproduce this issue, please use the example provided in this repository. Run npm run build followed by npm run preview. Create a single to do item and navigate to the url for that to do item. Prefix the url with view-source:, e.g. view-source:http://localhost:4173/todos/002d3845-5802-493f-a95c-d6f97da91905. Notice near the bottom: {trpc:new Map([])}.
This issue can further be debugged by adding return { trpc: trpcServer.hydrateToClient(event) } to +page.server.ts. Rerun the build and preview, and follow the same steps. You will end up with the following
data: [{type:"data",data:{trpc:new Map([])},uses:{url:1}},{type:"data",data:{trpc:new Map([[[["todos","get"],{input:{id:"002d3845-5802-493f-a95c-d6f97da91905"},type:"query"}],"Hi"]])},uses:{params:["id"],url:1}}]Notice how the first data node is an empty map, that being from +layout.server.ts, while the second being from +page.server.ts is properly populated.