Ant Ant - 12 days ago 5
React JSX Question

Server-side variables to Client-Side with React-Engine and Express

I'm reasonably new to React/React-Engine. I have a config on the server-side that I need to pass certain values of to client-side however I have a dependency on NODE_ENV in order to get the correct config out.

var config = {
local: { ... }
production: { ...}
}

module.exports = config[process.env.NODE_ENV]


Which works just fine server-side, however since I need to reference some of values contained in these objects on the client-side so I can't require(./config); in my React JSX.

Is there any easy way to pass this stuff into React? At the end of the day, I'd be happy if I could just pass the 'config' straight into React somehow without even having to worry about NODE_ENV on the client-side.

Thanks

Answer

The most common way to pass data from the server to client before rendering is to embed it in a global JavaScript variable on the page where your React is rendering.

So, for example, in the middleware where you're actually rendering some template which includes your <script> tags with your React app, you could add the info and grab it on the template:

var config = require('../config-from-somewhere');
app.get('/', function (req, res) {
  res.render('index', {config: JSON.stringify(config)});
});

And an example mustache template:

<html>
<body>
<script>
  window.CONFIG = JSON.parse({{{config}}});
</script>
<script src="my-react-app.js"/> 
</body>
</html>

HOWEVER apparently react-engine already provides its own way to send data do the client-side:

Data for component rendering

The actual data that gets fed into the component for rendering is the renderOptions object that express generates.

https://github.com/paypal/react-engine#data-for-component-rendering

As you can see in this example, the movies json is simply being passed into render:

app.get('*', function(req, res) {
  res.render(req.url, {
    movies: require('./movies.json')
  });
});

And then, by the grace of the framework's magic, probably on this line, the information is provided for your components and then the List uses it from props.movies.

module.exports = React.createClass({
  displayName: 'List',

  render: function render() {
    return (
      <div id='list'>
        <h1>Movies</h1>
        <h6>Click on a movie to see the details</h6>
        <ul>
          {this.props.movies.map(function(movie) {
            return (
              <li key={movie.id}>
                <Router.Link to={'/movie/' + movie.id}>
                  <img src={movie.image} alt={movie.title} />
                </Router.Link>
              </li>
            );
          })}

        </ul>
      </div>
    );
  }
});

So, basically add your config to your render call and it should be available in your component's props.

And for the very curious:

Indeed, as we can see on this line onwards, the engine merges renderOptions and res.locals and finally passes that down to React.

// create the data object that will be fed into the React render method.
// Data is a mash of the express' `render options` and `res.locals`
// and meta info about `react-engine`
var data = merge({
  __meta: {
    // get just the relative path for view file name
    view: null,
    markupId: Config.client.markupId
  }
}, omit(options, createOptions.renderOptionsKeysToFilter));

And:

return React.createElement(Component, merge({}, data, routerProps));
Comments