I'm having a very difficult time trying to figure out what we should do to make React isomorphic. I'm following some tutorials and that's what I have right now:
- I created a
server.js
file that starts an Express server - I created a
router.js
file where I created an Express Router object, imported theReactRouter
(react-router
) andReactDOMServer
(react-dom/server
) and there I set an Express route (router.get('*')
) that fetches the ReactRouter trying to find a route to render (ReactRouter.match
) - For bundling purposes I created a allRoutes.js file that contains the
ReactRouter
directives. I set two paths,/
and/about
- Two components,
Main
andAbout
, each with a button and a click event.Main
must be rendered in '/' and About in '/about'. Both components haveHTML
,HEAD
andBODY
tags and they refet tobundle.js
- app.js that sets React to the root-element in DOM.
- Webpack to create the
bundle.js
from the allRoutes.js file
I run into several problems, the first one was:
Main is correctly rendered and Javascript works fine, but going to
localhost:3000/about
, the page takes like 5 seconds to load, it loads with no javascript behavior and I get a checksum error.Removing bundle.js from /about solves the checksum and heavy loading problems, but there is no Javascript behavior
Bundling from allRoutes.js gives no errors like the above, but I have no javascript working in the page (though it's written in bundle.js)
Setting /about as a child route of /, since Main don't cause problems, never loads the About component. The server only renders Main if a route is Matched.
As I was planning it, but I'm not sure this is what I should do to achieve isomorphic behavior, I am creating an entire HTML page in each of these root-components, About and Main. It seems to me more SEO friendly.
Is there something I'm doing wrong?
app.js
var React = require('react');
var ReactDOM = require('react-dom');
var Main = require('./components/Main');
ReactDOM.render(React.createElement(Main),document);
router.js
var express = require('express');
var router = express.Router();
var ReactRouter = require('react-router');
var ReactDOMServer = require('react-dom/server');
var Main = require('./components/Main');
var About = require('./components/About');
var React = require('react');
var routes = require('./routes');
router.get('*', function (req, res) {
var props = {};
ReactRouter.match({
routes: (
routes
),
location: req.url
}, function (error, redirectLocation, renderProps) {
console.log(renderProps)
if (renderProps) {
// se encontrou uma rota, responde o HTML referente ao componente que foi preprocessado no backend
res.send(ReactDOMServer.renderToString(
<ReactRouter.RouterContext {...renderProps} createElement={
function(Component,renderProps){
return <Component {...renderProps} {...props} />
}
} />
));
} else {
res.status(404).send('Not found');
}
});
});
module.exports = router;
routes.js
//routes.js
var ReactRouter = require('react-router');
var React = require('react');
var Main = require('./components/Main');
var About = require('./components/About');
module.exports = (
<ReactRouter.Router history={ ReactRouter.browserHistory }>
<ReactRouter.Route path='/' component={ Main } />
<ReactRouter.Route path='/about' component={ About } />
</ReactRouter.Router>
);
webpack
module.exports = {
entry: './routes.js',
output: {
filename: './bundle.js',
path: 'public'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['react']
}
}
]
}
};
about.js
var React = require('react');
module.exports = React.createClass({
_handleClick: function(){
alert('Sobre!');
},
render: function(){
return (
<html>
<head>
<link rel='stylesheet' href='/style.css' />
</head>
<body>
<h1>About</h1>
<button onClick={ this._handleClick }>About</button>
<script src='/bundle.js'></script>
</body>
</html>
);
}
});
server.js
require('babel-register')({
presets: ['react']
});
var React = require('react');
var ReactDOMServer = require('react-dom/server');
var express = require('express');
var app = express();
app.use(express.static('./public'));
app.use(require('./router'));
app.listen(3000,function(){
console.log('Started at 3000');
});
@editing
I changed allRoutes (routes.js) to this.
<ReactRouter.Router history={ ReactRouter.browserHistory }>
<ReactRouter.Route path='/' component={ Main } >
<ReactRouter.Route path='about' component={ About } />
</ReactRouter.Route>
</ReactRouter.Router>
Now About is child of Main, Javascript works in Main, but About is never loaded. Trying to access a non-existing route shows me a 404, but any route that is mapped renders Main. What is going on?
@resolved found a solution: