React ReferenceError:未定义文档

时间:2016-05-10 05:45:59

标签: javascript reactjs redux isomorphic-javascript

我试图在服务器端渲染以下组件,作为通用/同构应用程序的一部分:

import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { GridLoader } from 'halogen';
import PostListItem from '../../components/PostListItem/PostListItem';
import { primary as color } from '../../colors';
import { changeSelectedPost, deletePostRequest } from '../../redux/modules/post';

export default connect()(class PostListView extends Component {
  static propTypes = {
    posts: ImmutablePropTypes.listOf(ImmutablePropTypes.contains({
      name: PropTypes.string.isRequired,
      title: PropTypes.string.isRequired,
      content: PropTypes.string.isRequired,
      slug: PropTypes.string.isRequired,
      id: PropTypes.string.isRequired,
    })).isRequired,
    loading: PropTypes.boolean.isRequired,
    dispatch: PropTypes.func.isRequired,
  }

  handleClick(post) {
    this.props.dispatch(changeSelectedPost(post));
  }

  handleDelete(post) {
    if (confirm('Do you want to delete this post')) { // eslint-disable-line
      this.props.dispatch(deletePostRequest(post));
    }
  }

  render() {
    if (typeof window !== 'undefined' && this.props.loading) {
      return (
        <div className="container">
          <GridLoader color={color} />
        </div>
      );
    }
    return (
      <div className="listView">
        {
          this.props.posts.toSeq().map((post, i, arr) => (
            <PostListItem
              post={post}
              key={i}
              onClick={this.handleClick.bind(this, post)}
              onDelete={this.handleDelete.bind(this, post)}
            />
          ))
        }
      </div>
    );
  }
});

但我收到错误:

module.exports = document.createElement('div').style;
             ^

ReferenceError: document is not defined

添加if块似乎已经绊倒了应用程序(应用程序在服务器和客户端之前完美呈现)。知道我的记录后,我很可能会遗漏一些非常明显的东西:P。有什么建议? :)

UPDATE:处理服务器端呈现的文件和服务器端的其余应用程序:

// server/server.js
'use-strict';

import path from 'path';
import bodyParser from 'body-parser';
import Express from 'express';
import React from 'react';
import webpack from 'webpack';
import { fromJS } from 'immutable';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import { match, RouterContext } from 'react-router';
import { Model } from 'objection';
import { Provider } from 'react-redux';
import { renderToString } from 'react-dom/server';
import config from '../webpack.config.dev';
import routes from '../shared/routes';
import configureStore from '../shared/redux/configureStore';
import assets from './assets';
import db from './db';
import posts from './routes/post.routes';
import Post from './models/post';
import serverConfig from './serverConfig';

// Initialize the Express App
const app = new Express();

Model.knex(db);

if (process.env.NODE_ENV !== 'production') {
  const compiler = webpack(config);
  app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }));
  app.use(webpackHotMiddleware(compiler));
}

// Apply body Parser and server public assets and routes
app.use(bodyParser.json({ limit: '20mb' }));
app.use(bodyParser.urlencoded({ limit: '20mb', extended: false }));

if (process.env.NODE_ENV !== 'production') {
  app.use(Express.static(path.resolve(__dirname, '../static')));
}

function getFilename() {
  if (process.env.NODE_ENV !== 'production') {
    return '"/dist/bundle.js"';
  }
  return `"/dist/${assets}"`;
}
app.use('/api', posts);

// Render Initial HTML
const renderFullPage = (html, initState, jsFile) => {
  return `
    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="/styles/normalize.css">
        <link rel="stylesheet" href="/styles/skeleton.css">
        <title>CeresShop</title>
      </head>
        <div id="root">${html}</div>
        <script>
          window.__INITIAL_STATE__ = ${JSON.stringify(initState)};
        </script>
        <script src=${jsFile}></script>
      </body>
    </html>
  `;
};

// Server Side Rendering based on routes matched by React-router.
app.use((req, res) => {
  match({ routes, location: req.url }, (err, redirectLocation, renderProps) => {
    if (err) {
      return res.status(500).end('Internal server error');
    }

    if (!renderProps) {
      return res.status(404).end('Not found!');
    }
    const initialState = fromJS({
      postReducer: {
        posts: [],
        post: {},
        loading: false,
      },
      route: {
        locationBeforeTransitions: null,
      },
    });

    async function loadData() {
      if (req.url.includes('post')) {
        try {
          const newSlug = req.url.substring(5).split('-');
          const newId = newSlug[newSlug.length - 1];
          const newPost = await Post.query().where('id', newId);
          const toBeProcessed = JSON.stringify(newPost[0]);
          return initialState.setIn(['postReducer', 'post'], fromJS(JSON.parse(toBeProcessed)));
        } catch (error) {
          console.log(error);
          return initialState;
        }
      }
      try {
        const newPosts = await Post.query();
        newPosts.sort((a, b) => b.dateadded - a.dateadded);
        const toBeProcessed = JSON.stringify(newPosts);
        return initialState.setIn(['postReducer', 'posts'], fromJS(JSON.parse(toBeProcessed)));
      } catch (error) {
        console.log(error);
        return initialState;
      }
    }

    loadData().then((currentState) => {
      const store = configureStore(currentState);
      const createElement = (Component, props) => (
        <Component
          {...props}
          radiumConfig={{ userAgent: req.headers['user-agent'] }}
        />
      );
      const initialView = renderToString(
        <Provider store={store}>
          <RouterContext {...renderProps} createElement={createElement} />
        </Provider>
      );
      const finalState = store.getState().toJS();

      res.status(200).end(renderFullPage(initialView, finalState, getFilename()));
    }).catch(err1 => {
      console.log(err1);
      res.end(renderFullPage(`Error: ${err1}`, {}, getFilename()));
    });
  });
});

// start app
app.listen(serverConfig.port, (error) => {
  if (!error) {
    console.log(`DAT SERVER is running on port: ${serverConfig.port}!`); // eslint-disable-line
  }
});

export default app;

1 个答案:

答案 0 :(得分:0)

如果您有一个正在呈现服务器端的组件需要另一个只会在客户端呈现的组件,它仍会尝试在服务器上导出该模块。您可以检查document是否存在,或者是否需要内联模块。

module.exports = typeof document !== 'undefined' ? document.createElement('div').style : null

// or

componentDidMount() {
  // this is only called in the client
  require('./clientComponent')
}