Reac-Router v4, рендеринг на стороне сервера, ReferenceError: документ не определен

Я использую реактивный маршрутизатор версии 4 и пытаюсь обрабатывать маршруты на сервере.

Все руководства, включая официальное руководство по рендерингу сервера в репозитории ReactTraining и руководство по React Router — рендеринг на стороне сервера используют React Router v3.

Единственное доступное руководство — это рендеринг на стороне сервера с помощью React Router версии 4.

Я выполнил те же шаги, но получил следующую ошибку:

 _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)

Вот моя сеть

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']),
      },
    ],
  },

}

И мой 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)

person Fakhruddin Abdi    schedule 22.11.2016    source источник
comment
Где в вашем проекте есть строка ReactDOM.render(<App />, document.getElementById('app'));? Единственная причина, по которой я мог видеть, что эта строка запускается на сервере, заключается в том, что она находится в вашем файле App.js, чего не должно быть.   -  person Paul S    schedule 22.11.2016
comment
Вы правы, Пол.. он существует в App.js.. но я не знаю, куда его поместить.   -  person Fakhruddin Abdi    schedule 22.11.2016
comment
Я только что начал создавать файл, а именно client.js, и установить его как точку входа для веб-пакета, а затем поместить туда строку. Вы правильно делаете?   -  person Fakhruddin Abdi    schedule 22.11.2016
comment
Это звучит правильно. ReactDOM.render — это функция на стороне клиента, поэтому она должна существовать в файле, который запускается только на стороне клиента.   -  person Paul S    schedule 22.11.2016


Ответы (2)


Я только что понял.!

Как упоминал @Paul S в комментариях, проблема возникает из-за размещения этой ссылки: ReactDOM.render(<App />, document.getElementById('app')); внутри компонента App.

Поэтому я просто создал еще один файл, а именно client.js, и переместил туда строку. Так что это работает.

Это потому, что компонент App используется совместно серверной и клиентской сторонами. Таким образом, вам нужно убрать клиентские коды из компонента App, если вы хотите иметь дело с рендерингом на стороне сервера.

Спасибо Полу.

person Fakhruddin Abdi    schedule 22.11.2016
comment
И тогда как именно вы вызываете этот файл и используете его? - person HalfWebDev; 09.02.2018
comment
Вы должны добавить client.js в свой webpack.config в качестве точки входа. Вы используете webpack? - person Fakhruddin Abdi; 09.02.2018

Я создал стартер на основе рендеринга на стороне сервера redux и документов рендеринга на стороне сервера react-router v4: https://github.com/gzoreslav/react-redux-saga-universal-application

person Zoreslav Goral    schedule 14.12.2017