javascriptreactjsreact-routerserver-rendering

Reac-Router v4, server side rendering, ReferenceError: document is not defined


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)

Solution

  • 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.