What is the recommended approach to handle data loading while using React and SSR without using Redux?
React, SSR & Data without Redux
It’s pretty straightforward with react-router. You use meteor/server-render
's onPageLoad
hook server side to match the route. Load data for matched components. Set a window
variable (window.__INITIAL_STATE__
) and then read that variable client-side and use it when you hydrate
.
Do you have an example repos that show this in action. Would love to check out something more fleshed out.
Redux makes this much easier and you should consider it, but here’s roughly how I would do it without redux.
// SERVER
import { Promise } from 'meteor/promise';
import { onPageLoad } from 'meteor/server-render';
import React from 'react';
import { Helmet } from 'react-helmet';
import { renderToString } from 'react-dom/server';
import StaticRouter from 'react-router-dom/StaticRouter';
import { matchRoutes, renderRoutes } from 'react-router-config';
import routes from './imports/ui/routes';
onPageLoad(async sink => {
const { request: { url, browser }, arch } = sink;
if (arch === 'web.browser') {
const branch = matchRoutes(routes, url.pathname);
const promises = branch.map(({ route, match }) => {
const { fetchData } = route.component;
return fetchData instanceof Function ? fetchData(match) : Promise.resolve(null);
});
const [fetchedData] = await Promise.all(promises);
const context = {};
const content = renderToString(
<StaticRouter location={url} context={context}>
{renderRoutes(routes, fetchedData)}
</StaticRouter>
);
sink.renderIntoElementById('react-root', content);
const helmet = Helmet.renderStatic();
sink.appendToHead(`
${helmet.title.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(store.getState())};
</script>
`);
}
});
// CLIENT
import React from 'react';
import { hydrate } from 'react-dom';
import BrowserRouter from 'react-router-dom/BrowserRouter';
import { renderRoutes } from 'react-router-config';
import routes from './imports/ui/routes';
const fetchedData = window.__INITIAL_STORE__;
delete window.__INITIAL_STORE__;
hydrate(
<BrowserRouter>
{renderRoutes(routes, fetchedData)}
</BrowserRouter>,
document.getElementById('react-root'),
);
A component might look like this:
class ExamplePage extends Component {
static async fetchData({ params: { urlParam } }) {
return fetchTheThingUsingAParam(urlParam);
}
...
How to use React SSR for just a single route?
I’ve asked this question before and got no response so i came up with my own, slightly different solution which I explained here: React SSR data hydration help.
My solution is completely self-contained within any component which uses it, and the hydrated data is silently replaced with the live subscription once the client is loaded.
I created an example repo here: https://github.com/wildhart/viewmodel-react-starter-meteor-ssr