React-starter-kit: pass data from server.js to client side component

Created on 30 Mar 2016  路  7Comments  路  Source: kriasoft/react-starter-kit

Hello, I am trying to pass some data to client side components from the server.
My current approach is, when registering server side middleware I try to pass additional data to the context.

const context = {
  insertCss: styles => css.push(styles._getCss()),
  onSetTitle: value => (data.title = value),
  onSetMeta: (key, value) => (data[key] = value),
  onPageNotFound: () => (statusCode = 404),
  serverData: resp,
};

when I log during render on the server side the data is there. but when the client kicks in, my guess is that it is being overwritten.

Same happens inside the routes.js:

on('*', async (state, next) => {
  const component = await next();
    return component && <App path={state.path} context={state.context}>{component}</App>;
});

Server side state.context.serverData is there, but on the client side it is undefined. I kinda need to use res.redirect before the header is set, so fetch will not really work for me in this situation.

Here is a full example of what I am trying to achieve:

//
// Register server-side rendering middleware
// -----------------------------------------------------------------------------
server.get('*', async (req, res, next) => {
  const respond = (authorized, resp) => {
    try {
      let statusCode = 200;
      const template = require('./views/index.jade');
      const data = { title: '', description: '', css: '', body: '', entry: assets.main.js };

      if (process.env.NODE_ENV === 'production') {
        data.trackingId = analytics.google.trackingId;
      }

      const css = [];
      const context = {
        insertCss: styles => css.push(styles._getCss()),
        onSetTitle: value => (data.title = value),
        onSetMeta: (key, value) => (data[key] = value),
        onPageNotFound: () => (statusCode = 404),
        serverData: resp,
      };

      /* await */ Router.dispatch({path: req.path, query: req.query, context }, (state, component) => {
        data.body = ReactDOM.renderToString(component);
        data.css = css.join('');
      });

      res.status(statusCode);
      (authorized == true ? res.send(template(data)) : res.redirect('/login'));
    } catch (err) {
      next(err);
    }
  }
  if(req.path !== '/login'){
    validateSession(req.session, (resp) => {
      respond(true, resp);
    }, () => {
      respond(false);
    });
  } else {
    respond(true);
  }

});

Most helpful comment

One solution is to update the "shell" HTML template to include something like this:

<script>window.appData = {{appData}};</script>

Then update server.js to pass the initial application's state to that template:

data.appData = JSON.stringify({ foo: 123 });

Then update client.js to pass data from that window.appData variable, to React app via context.

All 7 comments

One solution is to update the "shell" HTML template to include something like this:

<script>window.appData = {{appData}};</script>

Then update server.js to pass the initial application's state to that template:

data.appData = JSON.stringify({ foo: 123 });

Then update client.js to pass data from that window.appData variable, to React app via context.

Thank You Koistya! I actually considered this approach before. Worked well for me this time.

@audi70r another pattern you may want to consider, is to modify src/core/fetch HTTP client utility to accept a context / cache variable as the first parameter, which can be used for caching all the HTTP requests made during server-side rendering, that cached is passed to the client app using the pattern above, and on the client this src/core/fetch HTTP client utility can cached responses during the initial page load. And after the initial page load completes, that cache is destroyed and all the Ajax requests go as regular.

PR with this feature is welcome :)

@Bogdaan I will check that one, thanks!

@koistya is the method you described above (passing data through window) really the correct way to do this?

I'm new to this and would just like to know if that method is considered correct, or is more of a hack.

@joenot443 yes, it is ok. But do not forget about XSS protection, for example: https://github.com/kriasoft/react-starter-kit/pull/883 uses serialize-javascript for that.

Was this page helpful?
0 / 5 - 0 ratings