Reputation: 4795
Everything is about isomorphic application. I'm using React
with react-router
module on server side for routing purposes and have following warning in browser console.
Warning: render(...): Replacing React-rendered children with a new root component. If you intended to update the children of this node, you should instead have the existing children update their state and render the new components instead of calling ReactDOM.render.
I have following routes schema defined on backend:
<Route path="/" component={App} >
<IndexRoute component={Home} />
</Route>
App component:
module.exports = React.createClass({
render : function() {
return <html>
<head></head>
<body>
<div id="container">
{ this.props.children }
</div>
<script src="/app/bundle.js"></script>
</body>
</html>
}
});
Home component:
module.exports = React.createClass({
render : function() {
return <div>Any content here</div>
}
});
After that I use on the frontend:
ReactDOM.render(<Home />, document.getElementById('container'));
Probable solution:
If I understood correctly if I could render App component as static markup(renderToStaticMarkup
) and Home component as a string (renderToString
), then it would be ok.
Is it possible to implement something like that with react-router
?
Upvotes: 0
Views: 1354
Reputation: 12552
Assuming RR 1.03, your routing configuration looks fine.
Your app component should be like this:
module.exports = React.createClass({
render : function() {
return <html>
<head></head>
<body>
<div id="container">
{React.cloneElement(this.props.children,
{
anyOtherPropsHere: 'blablah'
}
)}
</div>
<script src="/app/bundle.js"></script>
</body>
</html>
}
});
Your server response -> render should look something like this. (taken from the documentation)
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')
}
})
})
And finally, somewhere on clientside load, do something like this. I've added the history library in this example to help.
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, match, RoutingContext } from 'react-router';
import history from 'utils/history';
import AppRoutes from '/app/AppRoutes';
// use this function to return a Route component with the right props
function createFluxComponent(Component, props) {
props = Object.assign(props, {
flux: window.flux.or.whatevs
});
return <Component {...props} />;
}
// add a global history listener perhaps?
history.listen(function(location) {
console.log('Transition to--------------', location.pathname);
});
// add window.router polyfill for transitionTo
window.router = {
transitionTo: function(t) {
return history.pushState(null, t);
}
};
// render your routing configuration, history and flux as props
ReactDOM.render(<Router createElement={createFluxComponent} history={history} routes={AppRoutes}/>, document);
What is most important is that you render to string using the RouterContext and render props on the server side. You get the renderProps from the RR match function, which will run your routes against the config and produce the right component in renderProps. Then you'll simply render the client side with Router element and the router config to the document. Does this make sense? It should work without any invariants.
Upvotes: 3