1

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 the ReactRouter (react-router) and ReactDOMServer (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 and About, each with a button and a click event. Main must be rendered in '/' and About in '/about'. Both components have HTML, HEAD and BODY tags and they refet to bundle.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:

https://github.com/victorsferreira/react-isormorphic/

Victor Ferreira
  • 6,151
  • 13
  • 64
  • 120

0 Answers0