React, SSR & Data without Redux


#1

What is the recommended approach to handle data loading while using React and SSR without using Redux?


#2

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.


#3

Do you have an example repos that show this in action. Would love to check out something more fleshed out.


#4

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?
#5

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


#6

mobix
more chars and more


#7

Appreciate it. I have added Redux and have SSR working now. Thanks!