ThomasReggi
ThomasReggi

Reputation: 59345

React Router server rendering with server props

Here's an example from the docs.

import { renderToString } from 'react-dom/server'
import { match, RouterContext } from 'react-router'
import routes from './routes'

serve((req, res) => {
  // Note that req.url here should be the full URL path from
  // the original request, including the query string.
  match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
    if (error) {
      res.status(500).send(error.message)
    } else if (redirectLocation) {
      res.redirect(302, redirectLocation.pathname + redirectLocation.search)
    } else if (renderProps) {
      // You can also check renderProps.components or renderProps.routes for
      // your "not found" component or route respectively, and send a 404 as
      // below, if you're using a catch-all route.
      res.status(200).send(renderToString(<RouterContext {...renderProps} />))
    } else {
      res.status(404).send('Not found')
    }
  })
})

I have an object like this:

let reactServerProps = {
  'gaKey': process.env.GA_KEY,
  'query': req.query,
}

I'm trying to pass this object into react router here

res.status(200).send(renderToString(<RouterContext {...renderProps} {...reactServerProps} />))

And I can't seem to provide access to the variables from within my components.

Upvotes: 3

Views: 722

Answers (2)

Yasin Yaqoobi
Yasin Yaqoobi

Reputation: 2040

A much simpler solution is to put whatever data you want in props.routes and it will be passed to your component where you can access it directly through

props.routes.[whatever you passed in]

So in the serve function you can do

props.routes.reactServerProps = {
'gaKey': process.env.GA_KEY,
'query': req.query
}

and in your component you can access it with props.routes.reactServerProps.

See the very last comment by Ryan Florence. https://github.com/ReactTraining/react-router/issues/1017

He forgot the (s). Won't work with route.

Upvotes: -1

Brad Colthurst
Brad Colthurst

Reputation: 2605

The issue is that <ReactContext /> only passes some react-router pre-defined props down to the component tree that it builds rather than the custom props you might expect in a normal component.

There are a few workarounds to this issue, though none of them are particularly pretty. The most widely used I think I've seen is to wrap <ReactContext /> with a component whose sole purpose is to make use of React's context feature & pass contextual data down to its children rather than props.

So:

import React from 'react';

export default class DataWrapper extends React.Component {

getChildContext () {

    return {
        data: this.props.data
    };

}

render () {

    return this.props.children;
}
}

Then in your express server:

// Grab the data from wherever you need then pass it to your <DataWrapper /> as a prop 
// which in turn will pass it down to all it's children through context
res.status(200).send(renderToString(<DataWrapper data={data}><RouterContext {...renderProps} /></DataWrapper>))

And you should then be able to access that data in your child components:

export default class SomeChildComponent extends React.Component {

constructor (props, context) {

    super(props, context);
    this.state = {
        gaKey: context.data.gaKey,
        query: context.data.query
    };
}

};

I know it used to be possible to make use of the createElement method to set some custom props and pass them through to your child routes in a similar fashion, but I'm not positive that's still valid in newer versions of react-router. See: https://github.com/reactjs/react-router/issues/1369


UPDATE: It is still possible to use middleware to pass additional values into route components. via: https://github.com/reactjs/react-router/issues/3183

And you should be able to make use of props over context:

function createElementFn(serverProps) {
  return function(Component, props) {
    return <Component {...serverProps} {...props} />
  }
} 

Then add createElement in your <RouterContext /> like so, passing it your serverProps:

res.status(200).send(renderToString(<RouterContext {...renderProps} createElement={createElementFn(serverProps)} />)) 

And access them in any of your child components with a simple this.props.gaKey & this.props.query

Upvotes: 3

Related Questions