【问题标题】:ReactJS error when accessing page using Server-side Rendering使用服务器端渲染访问页面时出现 ReactJS 错误
【发布时间】:2020-05-16 02:55:36
【问题描述】:

我在加载 ReactJS 页面时收到此错误

Error: Invariant failed: You should not use <Switch> outside a <Router>
    at invariant (/home/user/Documents/Development/hmuweb/room/node_modules/tiny-invariant/dist/tiny-invariant.cjs.js:13:11)
    at Object.children (/home/user/Documents/Development/hmuweb/room/node_modules/react-router/cjs/react-router.js:685:19)
    at ReactDOMServerRenderer.render (/home/user/Documents/Development/hmuweb/room/node_modules/react-dom/cjs/react-dom-server.node.development.js:3635:55)
    at ReactDOMServerRenderer.read (/home/user/Documents/Development/hmuweb/room/node_modules/react-dom/cjs/react-dom-server.node.development.js:3373:29)
    at Object.renderToString (/home/user/Documents/Development/hmuweb/room/node_modules/react-dom/cjs/react-dom-server.node.development.js:3988:27)
    at ./server/index.js.app.get (/home/user/Documents/Development/hmuweb/room/server-build/index.js:215:71)
    at Layer.handle [as handle_request] (/home/user/Documents/Development/hmuweb/room/node_modules/express/lib/router/layer.js:95:5)
    at next (/home/user/Documents/Development/hmuweb/room/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/home/user/Documents/Development/hmuweb/room/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/home/user/Documents/Development/hmuweb/room/node_modules/express/lib/router/layer.js:95:5)

我假设此错误消息来自我的 App.js 文件,但我似乎无法找到错误发生的确切位置。我尝试重新构建我的 App.js,但是当我这样做时,我收到另一个错误,说道具历史在路由器中被标记为必需,但它的值是未定义的。此外,它无法读取未定义的属性“位置”。知道如何解决这个问题吗?它从我实现 SSR(服务器端渲染)开始。

import React, { Component } from 'react';
import { Route, Switch, BrowserRouter as Router } from 'react-router-dom';
import './App.css';
import Room from './App/pages/Room'
import Content from './App/pages/content';

class App extends Component {
  render() {
    return (
      <div>
        <Switch>
          <Route exact={true} path="/" component={Room} />
          <Route path="/watch" component={Content} />
        </Switch>
      </div>
    );
  }
}
export default App;

需要 webpack.server.js 文件

const path = require('path');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  devtool: 'source-map',
  entry: './server/index.js',

  target: 'node',

  externals: [nodeExternals()],

  module: {
    rules: [
      {
        test: /\.js$/,
        use: ["babel-loader"],
        exclude: /node_modules/,
      },
      { test: /\.css$/, loader: "css-loader" },
      { test: /\.(jpg|png|svg)$/, use: 'file-loader'}
    ]
  },
  resolve: {
    alias: {        
        'react-router-dom': path.join('./node_modules/react-router-dom')
    }
  },
  output: {
    path: path.resolve('server-build'),
    filename: 'index.js'
  },

};

需要的 package.json 文件

{
  "name": "room",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@babel/plugin-proposal-class-properties": "^7.8.3",
    "axios": "^0.19.2",
    "babel-preset-es2015": "^6.24.1",
    "bootstrap": "^3.4.1",
    "branca": "^0.3.0",
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "hls.js": "^0.13.2",
    "html-react-parser": "^0.10.3",
    "jquery": "^3.5.1",
    "jsonwebtoken": "^8.5.1",
    "object-encrypt-decrypt": "^1.0.2",
    "path": "^0.12.7",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-helmet": "^6.0.0",
    "react-router-dom": "^5.2.0",
    "react-router-redux": "^4.0.8",
    "react-scripts": "3.2.0"
  },
  "scripts": {
    "start": "node ./server/index.js | react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "dev:build-server": "NODE_ENV=development webpack --config webpack.server.js --mode=development -w",
    "dev:start": "nodemon ./server-build/index.js",
    "dev": "npm-run-all --parallel build dev:*"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "proxy": "http://localhost:5000",
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react-app": "^9.1.2",
    "nodemon": "^2.0.4",
    "npm-run-all": "^4.1.5",
    "webpack-cli": "^3.3.11",
    "webpack-node-externals": "^1.7.2"
  },
  "babel": {
    "presets": [
      "@babel/preset-env",
      "@babel/preset-react"
    ],
    "plugins": [
      "@babel/plugin-proposal-class-properties"
    ]
  }
}

索引.js

import path from 'path';
import fs from 'fs';
import express from 'express';
import React from 'react'
import ReactDOMServer from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import App from '../src/App';
const PORT = process.env.PORT || 5000;
const app = express();

app.use(express.static(path.join(__dirname, '../build')));

app.get('/*', (req, res) => {
  const context = {};
  const app = ReactDOMServer.renderToString(
    <StaticRouter location={req.url} context={context}>
      <App />
    </StaticRouter>
  );
  const indexFile = path.join(__dirname + '../build/index.html');
  fs.readFile(indexFile, 'utf8', (err, data) => {
    if (err) {
      console.error('Something went wrong:', err);
      return res.status(500).send('Oops, better luck next time!');
    }

    if (context.status === 404) {
      res.status(404);
    }

    return res.send(
      data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
    );
  });
});

app.listen(PORT, () => {
  console.log(`???? Server is listening on port ${PORT}`);
});

【问题讨论】:

  • 您的应用程序中的组件路由器在哪里?该错误表明 react router DOM 中的路由器组件丢失,是否在另一个组件中?
  • @AlexanderStaroselsky 我很抱歉没有包含我的 index.js。请查看我上面的更新代码
  • 好的,我看到你导入了它,但你没有在里面包装 App?您实际在哪里使用该组件?
  • 据我所知,没有其他地方可以导入路由器组件。我只使用了 Switch、Route 和 BrowserRouter,并且都在上面的 index.js 和 App.js 文件中导入。我什至在目录中搜索。 @AlexanderStaroselsky
  • 正确的错误是说 Switch 需要在 BrowserRouter、HashRouter 或 Router 的某个级别有一个父包装元素。你没有那个。您需要添加它。你实际上已经在 index.js 中导入了它,但实际上并没有放入 render 中。用 BrowserRouter 包装应用程序。

标签: reactjs react-router babeljs


【解决方案1】:

经过几个小时的故障排除,我终于解决了这个问题。

您必须将它嵌套在一个 StaticRouter 标签内,而不是直接在 renderToString 函数中传递。

这个答案帮助了我:https://github.com/jaredpalmer/razzle/issues/1157#issuecomment-553044993

另外,我必须将我的 Switch 标记嵌套在 Router 标记中,如下所示:

<Router>
  <Switch>
    <Route exact={true} path="/" component={Room} />
    <Route path="/watch" component={Content} />
  </Switch>
</Router>

执行此操作后,我收到了另一个问题,即找不到我的构建 index.html。为了解决这个问题,我不得不调整我的项目:

  1. 在 ReactJS index.js 文件中,我不得不将我的 ReactDOM.render 切换为 ReactDOM.hydra。

  2. 在我的服务器文件夹中创建了一个 server.js 文件,我将 index.js 代码移到其中。

  3. 在我的旧 server/index.js 中,我使用 @babel 实现了对 JSX 的服务器端支持。

这是我的代码:

服务器/index.js

import path from 'path';
import fs from 'fs';
import express from 'express';
import React from 'react'
import ReactDOMServer from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import App from '../src/App';
const PORT = process.env.PORT || 5000;
const app = express();

app.get('^/$', (req, res) => {
  const context = {};
  const app = ReactDOMServer.renderToString(
    <StaticRouter location={req.url} context={context}>
      <App />
    </StaticRouter>
  );
  const indexFile = path.resolve('./build/index.html');
  fs.readFile(indexFile, 'utf8', (err, data) => {
    if (err) {
      console.error('Something went wrong:', err);
      return res.status(500).send('Oops, better luck next time!');
    }

    if (context.status === 404) {
      res.status(404);
    }

    return res.send(
      data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
    );
  });
});

app.use(express.static(path.resolve(__dirname, '..', 'build')));

app.listen(PORT, () => {
  console.log(`? Server is listening on port ${PORT}`);
});

服务器/index.js

require('ignore-styles')

require('@babel/register')({
  ignore: [/(node_module)/],
  presets: ['@babel/preset-env', '@babel/preset-react'],
  plugins: ['@babel/plugin-proposal-class-properties']
})

require('./server');

package.json

{
  "name": "room",
  "version": "0.1.0",
  "private": true,
  "homepage": "https://hmutv.com/",
  "dependencies": {
    "@babel/plugin-proposal-class-properties": "^7.8.3",
    "@babel/preset-react": "^7.9.4",
    "@babel/register": "^7.9.0",
    "axios": "^0.19.2",
    "babel-preset-es2015": "^6.24.1",
    "bootstrap": "^3.4.1",
    "branca": "^0.3.0",
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "history": "^4.10.1",
    "hls.js": "^0.13.2",
    "html-react-parser": "^0.10.3",
    "ignore-styles": "^5.0.1",
    "jquery": "^3.5.1",
    "jsonwebtoken": "^8.5.1",
    "next": "^9.4.0",
    "object-encrypt-decrypt": "^1.0.2",
    "path": "^0.12.7",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-helmet": "^6.0.0",
    "react-router-dom": "^5.2.0",
    "react-router-redux": "^4.0.8",
    "react-scripts": "3.2.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "ssr": "node server/index.js"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "proxy": "http://localhost:5000",
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react-app": "^9.1.2",
    "nodemon": "^2.0.4",
    "npm-run-all": "^4.1.5",
    "webpack-cli": "^3.3.11",
    "webpack-node-externals": "^1.7.2"
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 2017-04-26
    相关资源
    最近更新 更多