4
votes

I'm trying to build my first server side rendering with ReactJS. I've based my code from this Redux tutorial

The application is working fine when running npm start (webpack && node server.js).

I want to use hot reloading to be able to do an application fast debugging on dev environment. So I've added the start option on my npm configuration (webpack-dev-server --inline --hot). Notice that I don't have a index.html file as react-router is choosing the proper JSX file to load.

npm start opens my public folder and show files, but does no start my application. Normally I would click on a index.html file, but I have none.

I can't understand what's going on and don't know how to solve. Help appreciated.

package.json:

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config webpack.config.prod.js -p",
    "start": "webpack-dev-server --inline --hot",
    "dev": "webpack && node server.js"
  },

webpack.config.js:

module.exports = {
    entry: './client.js',
    output: {
        filename: 'bundle.js',
        path: __dirname + '/public'
    },
    module: {
        loaders: [
            {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                query: {
                    presets: ['react', 'es2015', 'stage-0']
                }
            }
        ]
    }
};

server.js

require('babel-register')({
    presets: ['react', 'es2015', 'stage-0']
});

var express = require('express');
var config = require('config');
var app = express();

var serverConfig = config.get('serverConfig');

app.use(express.static('public'));
app.use(require('./routes/index.jsx'));

app.listen(serverConfig.port, function () {
    console.log('FactoryWays server running on port ' + serverConfig.port);
    console.log('Press CTRL-C to stop the server.');
});

client.js

var ReactDOM = require('react-dom');
var React = require('react');
var routes = require('./routes/routes.jsx');
var Redux = require('redux');
var Provider = require('react-redux').Provider;

function reducer(state) { return state; }

var store = Redux.createStore(reducer, window.PROPS);

ReactDOM.render(
    <Provider store={store}>
        {routes}
    </Provider>, document
);

routes/index.jsx:

var router = require('express').Router();
var React = require('react');
var ReactDOMServer = require('react-dom/server');
var ReactRouter = require('react-router');
var Redux = require('redux');
var Provider = require('react-redux').Provider;

function reducer(state) { return state; }

router.get('*', function(request, response) {
    var initialState = { title: 'Universal React' };
    var store = Redux.createStore(reducer, initialState);

    ReactRouter.match({
        routes: require('./routes.jsx'),
        location: request.url
    }, function(error, redirectLocation, renderProps) {
        if (renderProps) {
            var html = ReactDOMServer.renderToString(
                <Provider store={store}>
                    <ReactRouter.RouterContext {...renderProps} />
                </Provider>
            );
            response.send(html);
        } else {
            response.status(404).send('Not Found');
        }
    });
});

module.exports = router;

routes/routes.jsx:

var React = require('react');
var ReactRouter = require('react-router');
var Router = ReactRouter.Router;
var Route = ReactRouter.Route;
var IndexRoute = ReactRouter.IndexRoute;
var browserHistory = ReactRouter.browserHistory;

module.exports = (
    <Router history={browserHistory}>
        <Route path='/' component={require('../views/Layout.jsx')}>
            <IndexRoute component={require('../views/Index.jsx')} />
        </Route>
    </Router>
);

views/Layout.jsx:

var React = require('react');
var Link = require('react-router').Link;
var connect = require('react-redux').connect;

var Layout = React.createClass({
    _handleClick: function() {
        alert();
    },
    render: function() {
        var custom = this.props.custom;
        return (
            <html>
                <head>
                    <title>{custom.title}</title>
                    <link rel='stylesheet' href='/style.css' />
                </head>
                <body>
                    <h1>{custom.title}</h1>
                    <p>Isn't server-side rendering remarkable?</p>
                    <button onClick={this._handleClick}>Click Me</button>
                    {this.props.children}
                    <ul>
                        <li>
                            <Link to='/'>Home</Link>
                        </li>
                        <li>
                            <Link to='/about'>About</Link>
                        </li>
                    </ul>
                    <script dangerouslySetInnerHTML={{
                        __html: 'window.PROPS=' + JSON.stringify(custom)
                    }} />
                    <script src='/bundle.js' />
                </body>
            </html>
        );
    }
});

var wrapper = connect(
    function(state) {
        return { custom: state };
    }
);

module.exports = wrapper(Layout);
1

1 Answers

3
votes

Notice that I don't have a index.html file as react-router is choosing the proper JSX file to load

You still need one if you're running webpack-dev-server.

Webpack is used to bundle JavaScript together. That's it. In your case (based on your webpack.config.js file), it's outputting a bundle.js file in your public directory.

Webpack Dev Server is a simple static file server to aid development. It doesn't know anything about your server configuration in server.js.

Your two commands in your package.json require different setups:

npm start

npm start runs webpack-dev-server. By default, webpack-dev-server will serve the contents of your current directory, so you'll need to put your an index.html in the root directory of your project. The contents of index.html are simple: just boilerplate HTML that includes a script tag linking to your bundle.js file.

npm dev

When you're running npm dev however, you're rolling your own server, defined in server.js. In that case it's configured to generate and serve an index.html (as well as all the other static files you need) for you.

If you want to do hot-reloading and run your own server, you can add webpack-dev-middleware to your Express server.