Incorporating async logic in Server side rendering is a bit tricky. When you call renderToString(), you will get the html for the initial render(). What you need is the html for subsequent render() which happens when the data comes in.
The basic idea is the following:
- Execute the API calls
- Wait for them to finish
- Return html for final render()
You can try the approach mentioned on Redux website (https://redux.js.org/recipes/server-rendering#async-state-fetching) or on this tutorial (https://www.sitepoint.com/asynchronous-apis-server-rendered-react/).
I prefer the latter, it's more generic and you don't need different fetching code for each component. You might want to tweak the code to make it simpler though. More details on react router's context API here : https://medium.freecodecamp.org/how-to-protect-your-routes-with-react-context-717670c4713a
function handleRender(req, res) {
const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
console.log('fullUrl: ', fullUrl);
console.log('req.url: ', req.url);
// Create a new Redux store instance
const store = createStore(reducerFn);
const context = {}; // Store stuff here
const urlToRender = req.url;
// Render the component to a string
const html1 = renderToString(
<Provider store={store}>
<StaticRouter location={urlToRender} context={context}>
{routes}
</StaticRouter>
</Provider>
);
// Get promises from context, wait for all of them to finish
// Render again
const html = renderToString(
<Provider store={store}>
<StaticRouter location={urlToRender} context={context}>
{routes}
</StaticRouter>
</Provider>
);
const helmet = Helmet.renderStatic();
// Grab the initial state from our Redux store
const preloadedState = store.getState();
// Send the rendered page back to the client
res.send(renderFullPage(helmet, html, preloadedState));
}
I'll share the code for https://www.heloprotocol.in/ when I get a chance.