You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is using React 18, RTK beta, and cross-fetch with Webpack 5.
I've been digging into SSR for a few days now and have it mostly working but could use some help to clarify how the implementation should look from a higher level. Meaning, how server render looks, then hydration, then client taking over, that whole flow. All of the examples I could find expect the use of Next.js or Redux Persist or something that already has hydration built in (which of course makes sense to have examples with popular libraries). But for those of us rolling our own hydration...
Get preloaded data from webpack/node build process - from json file
Dynamically import the RTK Query API - api file variable name from preloaded data
Initialize store - preloaded data gets passed here to become initial data
Use store to dispatch the initialize RTK Query API Queries
api.getRunningOperationPromises
renderToString
api.resetApiState
Return HTML string as server Response
Client
Get preloaded data from build - from window.__INITIAL_DATA__
Initialize store
Dispatch hydration action
Cleanup - delete window.__INITIAL_DATA__
Return <App />
Render
Dynamically import the RTK Query API - api file variable name from window.__INITIAL_DATA__.state.api
If server then hydrateRoot
If client then createRoot
The code itself looks something like this. This is pseudo-code to simplify so for sure there's obvious broken parts but the idea is just to get a grip on the overall flow. I put links below this to credit the parts of this code that have come from previous issue's discussions.
exportconsthydrate=createAction<DeepPartial<any>>('hydrate');constreducerWithHydrate=(preloadedState: DeepPartial<any>,combinedReducer: Reducer<any>)=>{returncreateReducer(preloadedState,(builder)=>{builder.addCase(hydrate,(state: RootState,action: AnyAction)=>{if(!action.isInitialHydrate){returnstate;}constnextState: RootState={ ...state, ...action.payload}// Exclude anything that's purely client state here by overriding what came from the action// if (state.stuff) nextState.stuff = state.stuff;returnnextState;}).addDefaultCase((state,action)=>combinedReducer(state,action));});};
In this case, hydration happens once when the client first renders from the server. After that when client routing takes over, hydration gets skipped and the rest of the store/state takes over as combinedReducers. I believe this is similar to the next.js way except for that client routing never takes over since nextjs SSRs every page.
There is lots of parts of this flow that I'm unsure of so any guidance is much appreciated.
Continuing with the Redux and RTK server rendering guides, at what points in the App should hydration be dispatched and under what conditions? In next.js it looks like shouldComponentUpdate triggers the dispatching of the hydration action at the _app and page level. Is there anywhere else it should be dispatched? How do we mimic shouldComponentUpdate for this case, without class components, in a more functional/hooks way? Instead of a HOC, manually placing these calls is fine but I'm not entirely sure where it/they should go.
What are the key points in server, render (as in webpack entry), and client that should have some Redux related functionality for handling SSR/hydration?
Does server and client in the "state-reconciliation-during-hydration" part of next-redux-wrapper refer to server being api/fetch/query cached calls and client being then simply the pure app/local/client data?
That is my best understanding of this SSR/hydration flow so far. After this point is a case-specific (i think) issue I've run into that may or may not end up having to do with RTK.
Where I'm at now, after I build the app with webpack and load the app for the first time, server side rendering works perfectly. When I refresh the page (even though it should be loading from the server the same way as the first time.. afaik), I get a flash of "error" in each component that's doing a query, followed by the queries re-fetching and then the page rendering correctly again. But the flash of "error" is a totally broken page until the queries finish again. I'm not sure why that's happening or if it's even related to this general SSR/hydration flow (could be on the cross-fetch side of things?) but right now I'm so unsure about so many parts of this that any guidance here to help me grasp this process will help regardless.
When I log the action.payload in the hydrate action reducer case during the refresh, it shows that the queries each have an error:
error: "TypeError: Only absolute URLs are supported"
status: "FETCH_ERROR"
Whereas for the first load, the queries successfully return data and correctly show up as rejected in Redux Dev Tools Inspector (since they are cached).
When I print the URL I'm passing, it is the full URL so I'm not sure why it's saying that. I have tested that on client and server (I use webpack env plugin to pass the URL as an environment variable to the client and the environment variable seems to print everywhere). I've also tried to pass the URL as a normal hard coded string but still receive this error.
So anyways, I thought I'd take a step back and try to understand this from a higher level to see if it leads me to what might be causing this. Even if this ends up being too case-specific to give an answer to, I thought writing this out would help me think this over and also might help jump start someone else heading down the same road :)
The text was updated successfully, but these errors were encountered:
This is using React 18, RTK beta, and cross-fetch with Webpack 5.
I've been digging into SSR for a few days now and have it mostly working but could use some help to clarify how the implementation should look from a higher level. Meaning, how server render looks, then hydration, then client taking over, that whole flow. All of the examples I could find expect the use of Next.js or Redux Persist or something that already has hydration built in (which of course makes sense to have examples with popular libraries). But for those of us rolling our own hydration...
I've started with the Redux Server Rendering Guide (https://redux.js.org/usage/server-rendering). That has been working really well.
Next I've gone through the RTK Query SSR guide (https://redux-toolkit.js.org/rtk-query/usage/server-side-rendering). That's gone well too but adding your own hydration to this example is where it starts to get tricky.
The flow I have so far looks something like this.
Server
Client
window.__INITIAL_DATA__
delete window.__INITIAL_DATA__
<App />
Render
window.__INITIAL_DATA__.state.api
The code itself looks something like this. This is pseudo-code to simplify so for sure there's obvious broken parts but the idea is just to get a grip on the overall flow. I put links below this to credit the parts of this code that have come from previous issue's discussions.
hooks.ts
hydrate.ts
fetch.ts
api.ts
store.ts
server.ts
App.client.tsx
entry.client.tsx - this is passed to webpack entry for client build
In this case, hydration happens once when the client first renders from the server. After that when client routing takes over, hydration gets skipped and the rest of the store/state takes over as combinedReducers. I believe this is similar to the next.js way except for that client routing never takes over since nextjs SSRs every page.
There is lots of parts of this flow that I'm unsure of so any guidance is much appreciated.
Some resources I've found really useful:
And of course:
Some questions I have are:
That is my best understanding of this SSR/hydration flow so far. After this point is a case-specific (i think) issue I've run into that may or may not end up having to do with RTK.
Where I'm at now, after I build the app with webpack and load the app for the first time, server side rendering works perfectly. When I refresh the page (even though it should be loading from the server the same way as the first time.. afaik), I get a flash of "error" in each component that's doing a query, followed by the queries re-fetching and then the page rendering correctly again. But the flash of "error" is a totally broken page until the queries finish again. I'm not sure why that's happening or if it's even related to this general SSR/hydration flow (could be on the cross-fetch side of things?) but right now I'm so unsure about so many parts of this that any guidance here to help me grasp this process will help regardless.
When I log the action.payload in the hydrate action reducer case during the refresh, it shows that the queries each have an error:
Whereas for the first load, the queries successfully return data and correctly show up as rejected in Redux Dev Tools Inspector (since they are cached).
When I print the URL I'm passing, it is the full URL so I'm not sure why it's saying that. I have tested that on client and server (I use webpack env plugin to pass the URL as an environment variable to the client and the environment variable seems to print everywhere). I've also tried to pass the URL as a normal hard coded string but still receive this error.
So anyways, I thought I'd take a step back and try to understand this from a higher level to see if it leads me to what might be causing this. Even if this ends up being too case-specific to give an answer to, I thought writing this out would help me think this over and also might help jump start someone else heading down the same road :)
The text was updated successfully, but these errors were encountered: