Influence the rendering order on Server Side on request
Unanswered
Cuvier’s Dwarf Caiman posted this in #help-forum
Cuvier’s Dwarf CaimanOP
Until now I never really had to think about the order how the server renders components on request ( Dynamic Server Side Rendering ) but our Application is far from conventional and now i'm faced with the following problem:
Short problem describtion:
When we look at the first picture I would expect the ServerStoreInitializer to run it's code on rendering before anything else. There wer do some calls we are necesary for the AppBody Component.
The Hirachy should be:
1. ServerStoreInitaliazer
- loads some data and the renders the clientStoreInitializer
2. AppBody with SideBar loads
But when we take a look at the logs: (second:miliseconds)
the sidebar get's rendered first.
Sidebar - 50:09.318
ServerStoreInitializer - 50:09.507
ClientStoreInitializer - 50:09.510
This leads to a hydration error because the store (which the client then use to fill the sidebar) has a different state, since it was loaded afterwards.
Is there a way to influence the order how nextjs, build the components?
Thank you!
Short problem describtion:
When we look at the first picture I would expect the ServerStoreInitializer to run it's code on rendering before anything else. There wer do some calls we are necesary for the AppBody Component.
The Hirachy should be:
1. ServerStoreInitaliazer
- loads some data and the renders the clientStoreInitializer
2. AppBody with SideBar loads
But when we take a look at the logs: (second:miliseconds)
the sidebar get's rendered first.
Sidebar - 50:09.318
ServerStoreInitializer - 50:09.507
ClientStoreInitializer - 50:09.510
This leads to a hydration error because the store (which the client then use to fill the sidebar) has a different state, since it was loaded afterwards.
Is there a way to influence the order how nextjs, build the components?
Thank you!
35 Replies
Cuvier’s Dwarf CaimanOP
here is another overview.
If the Sidebar is rendered before we run the StoreInitializer, the server will return a empty Sidebar but a Store with content and then the first draw on the client will have a sidebar with content. That's where the current hydration error is created
If the Sidebar is rendered before we run the StoreInitializer, the server will return a empty Sidebar but a Store with content and then the first draw on the client will have a sidebar with content. That's where the current hydration error is created
Cuvier’s Dwarf CaimanOP
actually, If I remove the Server StoreInitializer and put the await directly in the page.tsx everything works.
But that's not really what I want from a project structure point of view:
But that's not really what I want from a project structure point of view:
Cuvier’s Dwarf CaimanOP
bump
You should build your code in a way where order doesn't matter. Use cache() to dedupe calls so that order becomes irrelevant
Cuvier’s Dwarf CaimanOP
Can you explain a little more into your second point? I'm not sure how I could achieve the same outcome, as I displayed in the drawing (sending data on the first roundtrip and using that data to initalise a client store) in way that the order does not matter. As soon as I write a component which uses data from the store, the order matters.
Please correct my mental model if there is a better way to do that.
Please correct my mental model if there is a better way to do that.
can you give me the name of the function in <ServerStoreInitialiser> that needs to run its code before anything else? @Cuvier’s Dwarf Caiman
Server always runs first before client codes, just to put that here
Cuvier’s Dwarf CaimanOP
(That's obvious..)
yeah for obvious sake
So the problem now lies the different order ServerComponent gets run right
Cuvier’s Dwarf CaimanOP
and it has nothing to do with ClientStoreInitailizer
Cuvier’s Dwarf CaimanOP
I can't agree to that sentence. Yes, the problem is the order, if I try to structure my code, like shown above in the first view Screenshots.
Having a component on the Server that get's some data, set's the data in a store (the store on the server) and then fills a component (which runs on the client) which intialises the store on the client. Then we have a component which takes that store data to display it.
Having a component on the Server that get's some data, set's the data in a store (the store on the server) and then fills a component (which runs on the client) which intialises the store on the client. Then we have a component which takes that store data to display it.
Sorry, not very straight forward for me to explain the flow with words. Is something unclear from the drawing?
yes since Next.js interleaves client and server, it doesn't make sense to separate client and server to left and right
also server component cant use hooks
can I see the insides of ClientStoreInitializer
Cuvier’s Dwarf CaimanOP
But the goal is to display the two different render environments. Just saying it's 'interleaves' is too easy. My code runs once on the Server and once on the client. The data after creation on boths side's has to match, otherwise we have hydration errors.
Really not much interesting to see in the ClientStoreInitializer.
Really not much interesting to see in the ClientStoreInitializer.
not sure i fully understand the problem but heres a quick fix.
try wrapping your code that needs to be run once in your app with
try wrapping your code that needs to be run once in your app with
useEffectconst [mount, setMount] = useState(false)
useEffect(()=>{
if(!mount) setMount(true)
if(mount) // wirte code here....
},[mount])This ensures that the data is only run in the client and wont get prerendered in the server again
And regarding the order of server compoenents, like i said, you need to design your code such that the order of server execution doesn't matter in between components. Note that this is only talking within the scope of server-run functions.
Cuvier’s Dwarf CaimanOP
I fixed it already with the solution I provided above.
I want it to be prerendered in the Server again! That's the reason I want to use nextjs. On request:
1. get data from the backend
2. fill the store instance on the server with the data
3. prerender all components on the server
4. send the prerendered components to the client and from there on the client will get updated by websockets and the client side store.
I want it to be prerendered in the Server again! That's the reason I want to use nextjs. On request:
1. get data from the backend
2. fill the store instance on the server with the data
3. prerender all components on the server
4. send the prerendered components to the client and from there on the client will get updated by websockets and the client side store.
@Cuvier’s Dwarf Caiman I fixed it already with the solution I provided above.
I want it to be prerendered in the Server again! That's the reason I want to use nextjs. On request:
1. get data from the backend
2. fill the store instance on the server with the data
3. prerender all components on the server
4. send the prerendered components to the client and from there on the client will get updated by websockets and the client side store.
I know but Client components are prerendered on the server! you dont need this since you already get the data from ServerStoreInitializer!
Cuvier’s Dwarf CaimanOP
yes that is true. But the zustand store which get's initialized on the client, is empty. The Store can not be prerendered and Send. That's why it's important to build a component which is filled with the data and then onMount runs the code to initalise the client store.
And yes the components are prerendered, but since they react to the store, they use a hook to be reactive so they run again on the client. If the client store is empty then to rerun on the client would render an empty component.
And yes the components are prerendered, but since they react to the store, they use a hook to be reactive so they run again on the client. If the client store is empty then to rerun on the client would render an empty component.
Can you walk through what happen on request?
Cuvier’s Dwarf CaimanOP
1. A Zustand Store is created in the server environment, since all js runs on the server while prerendering
2. The ServerInitializer runs, fetches some data and initialize the store on the server.
3. It was important that 1 and 2 happen first and for the next steps the order does not matter
4. A component ClientInitializer will be build, that get's the same store data in a js script which will fill the fresh store on the client
4. Components who need the data from the store, like the list in my sidebar are consuming the store data and prerender their component on the server
5. send the response, forget everything and repeat from step 1 on the next request
2. The ServerInitializer runs, fetches some data and initialize the store on the server.
3. It was important that 1 and 2 happen first and for the next steps the order does not matter
4. A component ClientInitializer will be build, that get's the same store data in a js script which will fill the fresh store on the client
4. Components who need the data from the store, like the list in my sidebar are consuming the store data and prerender their component on the server
5. send the response, forget everything and repeat from step 1 on the next request
on the client:
1. get the prerendered components and display them.
2. after the js, components arrived and run, a new zustand store is created
3. the ClientStore Initializer runs, (which was passend and prerendered with the data as props from the server) and initializes the client store
4. the client components with "use client" run again, but show basically the same data because the client store is filled with the same data
1. get the prerendered components and display them.
2. after the js, components arrived and run, a new zustand store is created
3. the ClientStore Initializer runs, (which was passend and prerendered with the data as props from the server) and initializes the client store
4. the client components with "use client" run again, but show basically the same data because the client store is filled with the same data
You can design your code such that you dont need to ensure if 1 and 2 happens first by using memoization
Cuvier’s Dwarf CaimanOP
correct me if i'm wrong, but memoizatioin is just caching the result and only rerun the function if one of the dependencies changed and therefore probably the outcome should change. How would this fix a issue with the order on the first call? This would still not fix my problem, that the ServerInitializer runs after the SideBar is rendered... only now that it chached my result. So maybe the secons call does not have the problem anymore. But for the first call the order still matters.
This also brings the problem, that I would need to invalidate the cache at some point, but I want to assume that data is always stale, at request time
Thanks for your efforts, though.
React's memoization only cache up to a single request call. Its sole purpose is to dedupe calls within one single request
Cuvier’s Dwarf CaimanOP
Just bumping this, to be so fortunate to get this validated or another opinion, by somebody else from the comunity.