I'm using react-router version 4 and trying to handle routes on the server.
All tutorials including offical Server Rendering guide on ReactTraining repo and React Router tutorial - server side rendering are using React Router v3.
The only available guide out there is Server side rendering with React Router version 4.
I have followed same steps, but i got the following error :
_reactDom2.default.render(_react2.default.createElement(App, null), document.getElementById('app'));
^
ReferenceError: document is not defined
at Object.<anonymous> (/Users/fakhruddinabdi/Projects/fotomantic/fotomantic.com/public/server.bundle.js:271:70)
at __webpack_require__ (/Users/fakhruddinabdi/Projects/fotomantic/fotomantic.com/public/server.bundle.js:20:30)
at Object.<anonymous> (/Users/fakhruddinabdi/Projects/fotomantic/fotomantic.com/public/server.bundle.js:59:13)
at __webpack_require__ (/Users/fakhruddinabdi/Projects/fotomantic/fotomantic.com/public/server.bundle.js:20:30)
at /Users/fakhruddinabdi/Projects/fotomantic/fotomantic.com/public/server.bundle.js:40:18
at Object.<anonymous> (/Users/fakhruddinabdi/Projects/fotomantic/fotomantic.com/public/server.bundle.js:43:10)
at Module._compile (module.js:556:32)
at Object.Module._extensions..js (module.js:565:10)
at Module.load (module.js:473:32)
at tryModuleLoad (module.js:432:12)
Here is my web
var fs = require('fs');
var path = require('path');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: path.resolve(__dirname, 'server.js'),
output: {
filename: 'public/server.bundle.js',
},
plugins: [
new ExtractTextPlugin('public/styles.css'),
],
target: 'node',
// keep node_module paths out of the bundle
externals: fs.readdirSync(path.resolve(__dirname, 'node_modules')).concat([
'react-dom/server', 'react/addons',
]).reduce(function (ext, mod) {
ext[mod] = 'commonjs ' + mod
return ext
}, {}),
node: {
__filename: true,
__dirname: true
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
}, {
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/,
loader: 'url-loader?limit=10000&name=/[hash].[ext]',
}, {
test: /\.scss$/,
exclude: /node_modules/,
loader: ExtractTextPlugin.extract(['css', 'sass']),
},
],
},
}
And my server.js :
import { createServer } from 'http';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { ServerRouter, createServerRenderContext } from 'react-router';
import App from './src/components/App';
createServer((req, res) => {
// first create a context for <ServerRouter>, it's where we keep the
// results of rendering for the second pass if necessary
const context = createServerRenderContext()
// render the first time
let markup = renderToString(
<ServerRouter
location={req.url}
context={context}
>
<App/>
</ServerRouter>
)
// get the result
const result = context.getResult()
// the result will tell you if it redirected, if so, we ignore
// the markup and send a proper redirect.
if (result.redirect) {
res.writeHead(301, {
Location: result.redirect.pathname
})
res.end()
} else {
// the result will tell you if there were any misses, if so
// we can send a 404 and then do a second render pass with
// the context to clue the <Miss> components into rendering
// this time (on the client they know from componentDidMount)
if (result.missed) {
res.writeHead(404)
markup = renderToString(
<ServerRouter
location={req.url}
context={context}
>
<App />
</ServerRouter>
)
}
res.write(markup)
res.end()
}
}).listen(8080)
I just figured it out.!
As @Paul S mentioned in the comments the problem comes from putting this linke : ReactDOM.render(<App />, document.getElementById('app'));
inside the App
component.
So i just created another file namely client.js and and moved the line there. So it works.
Its because App
component is shared between server side and client side. So you need to take the client codes out of App
component if you want to deal with server side rendering.
Thanks to Paul.