【问题标题】:React SSR React Router Dom Switch, Route, Link "Invariant Failed"React SSR React Router Dom Switch, Route, Link "Invariant Failed"
【发布时间】:2021-01-28 07:28:50
【问题描述】:

我相信这个问题的正确描述是由 timdorr here 解释的。我尝试从包中导出 App.js,但得到窗口未定义错误。所以还是卡住了

SSR/Client React Router Dom “Switch” 因“Invariant Failed”而中断。我相信它说这与 Switch 不允许在“路由器”外部有关,它位于内部。

最小的项目链接如下,这可能是查看项目的更简单的方式。我在下面列出了主要文件

1:服务器端渲染端点

// EXPRESS ROUTER
const express = require("express");
const aRouter = express.Router();

// REACT UTILITIES
import React from "react";
import { renderToNodeStream } from "react-dom/server";
import { StaticRouter } from "react-router-dom";
import { createMemoryHistory } from "history";
import Loadable from "react-loadable";

// REDUX UTILITIES
import { init } from "../src/module/store";
import { Provider } from "react-redux";

// CUSTOM COMPONENTS
import App from "../src/App";

// UTILITIES
import fs from "fs-extra";
import renderUtils from "../utils/renderUtils";

// ASSETS
import { initState } from "../assets/store/init";

aRouter.get(["/", "/home"], async function (req, res) {

  console.log("RENDER HOME");

  try {

    // INITIAL WRITE TO CLIENT - START HEAD
    res.setHeader('Content-Type', 'text/html');
    res.write("<!DOCTYPE html>");
    res.write("<html style='scrollbar-width: none;'>");

    let headHTML = await fs.readFile("./public/head.html", "utf-8");
    let scriptsHTML = await fs.readFile("./public/scripts.html", "utf-8");

    res.write(headHTML);
    res.write("<body>");
    res.write(`<div id = "app-container">`);

    // INITALISING STATE
    var initialState = initState();
    initialState.article.articles = {
      "abcde": {
        title: "My First Article",
        body: "This is my first article"
      },
      "fghij": {
        title: "My Second Article",
        body: "This is my second article"
      },
      "klmno": {
        title: "My Third Article",
        body: "This is my third article"
      }
    };
    initialState.article.fetched = true;
    initialState.ui.user = { type: "" };
    initialState.ui.global = {
      team: "Arsenal",
      teamID: 19
    };

    const history = createMemoryHistory({ initialEntries: [req.originalUrl] });
    const store = init(history, initialState);
    
    // THE ISSUE SEEMS TO BE TO DO WITH THIS SERVER SIDE STATIC BROWSER AND THE CLIENT BORWSER ROUTER
    const stream = renderToNodeStream(
      <Provider store = {store}>
        <StaticRouter history = {history} location = {req.originalUrl} context = {{}}>
          <App />
        </StaticRouter>
      </Provider>
    );

    stream.pipe(res, { end: false });
    stream.on("end", renderUtils.onRenderEnd.bind(this, res, store, scriptsHTML));

  } catch (err) { renderUtils.onRenderError.bind(this, res, "RENDER HOME ERROR", err.message); }

});

var self = (module.exports = aRouter);

2:客户索引

// REACT
import React from "react";
import ReactDOM from "react-dom";
import Loadable from "react-loadable";
import { BrowserRouter } from "react-router-dom";
import { createMemoryHistory } from "history";
import { Provider } from "react-redux";

// REDUX
import { init } from "./module/store";

// CREATE STORE
let history = createMemoryHistory();
let store = init(history, window.INITIAL_STATE);

// MAIN APP COMPONENT
import App from "./App";

// MOUNTED STYLES
import "./style/client/index.scss";

const renderApp = () => {
  ReactDOM.hydrate(
    <Provider store = {store}>
      <BrowserRouter history = {history}>
        <App />
      </BrowserRouter>
    </Provider>,
    document.getElementById("app-container")
  );
};

store.subscribe(() => renderApp());

3:应用程序 - 客户端

// REACT
import React, { PureComponent } from "react";
import { Switch, Route } from "react-router-dom";
import PropTypes from "prop-types";

// REDUX STORE
import { connect } from "react-redux";
import { getName, getAge, getPosition } from "./module/user/userReducer";
import { getUIElement, setUIElement } from "./module/uiReducer";

// IMPORT CUSTOM COMPONENTS
import Routes from "./Routes";

class App extends PureComponent {

  componentDidMount = () => this.props.setUI("user", "type", "admin");

  render = () => {
    return (
      <div className = "app">
        <span>My App</span>
        <span>Name : {this.props.name}</span>
        <span>Age : {this.props.age}</span>
        <span>Position : {this.props.position}</span>
        <span>Team : {this.props.team}</span>
        <span>Team ID : {this.props.teamID}</span>
        <span>Type : {this.props.type}</span>
        <div>
          <Switch>
            <Route path = "/" component = {MyLocation} />
            <Route path = "/contact" render = {() => (<MyLocation location = "Contact" />)} />
          </Switch>
        </div>
      </div>
    );
  };

};

App.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number.isRequired,
  position: PropTypes.string.isRequired,
  team: PropTypes.string.isRequired,
  teamID: PropTypes.number.isRequired,
  type: PropTypes.string.isRequired,
  setUI: PropTypes.func.isRequired
};

const mapStateToProps = state => ({
  name: getName(state),
  age: getAge(state),
  position: getPosition(state),
  team: getUIElement(state, "global", "team"),
  teamID: getUIElement(state, "global", "teamID"),
  type: getUIElement(state, "user", "type")
});

const mapDispatchToProps = dispatch => ({
  setUI: (component, element, value) => dispatch(setUIElement({ component, element, value }))
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

完整的最小反应应用here

当我添加 Switch 和 Routes 时它会中断。开关“在”浏览器路由器内。我已经阅读了解释如何将相同的 React Router Dom 发送给客户端的文章,但我尝试了它们的解释,但它们对我不起作用。

要运行项目,只需运行“yarn”、“npm run build”和“npm start”。该应用程序只有一个页面,其中包含一些填充文本

问题:开关中断 要求:在 Switch 工作 已尝试:解释如何将相同的 react-router-dom 实例从服务器带到客户端并使用 webpack 别名等。

Timdorr(在问题开始时分享)解释了这一点。

我们在 5.0 中使用了一个新的 React.createContext API 来替换旧的上下文使用。这涉及创建“上下文”对、一组提供者和消费者组件。我们在一个单例模块中创建它,该模块直接导入到 Router 和 Link 中。在这个新 API 中,您必须使用完全相同的实例。两个单独创建的上下文永远不会匹配并且彼此不可见。

还有什么有趣的是,这可以在 Heroku “生产”上实时工作,但不能在本地“生产”上工作。我认为 heroku 有一些后备代码可以捕捉到它。

任何帮助都很好;

丹尼尔

【问题讨论】:

  • 也许尝试删除 node_modules 文件夹并更新所有/重新安装所有依赖项,然后确保您在所有文件中使用的是 BrowserRouter、Switch 和 Route 的确切版本。此外,在 APP-CLIENT 文件中,您实际上是在 browserRouter 之外使用 switch,尝试将其设置为:&lt;BrowserRouter&gt; &lt;Switch&gt; ...,这意味着将 switch 设置为路由器的直接子级。我不是说它会解决问题,但尝试不会伤害:)
  • 嗨。顺便说一句,我没有在 BrowserRouter 之外使用 Switch。这也不起作用,但感谢您的评论

标签: reactjs react-router-dom server-side-rendering


【解决方案1】:

玩了很久才发现问题出在

import { withRouter } from "react-router-dom";

export default withRouter(Component);

我认为 withRouter 在这个新版本中不再存在,至于为什么它与 Heroku 一起工作是一个谜。我认为 Heroku 有很好的版本控制和调试/处理。

我开始改用钩子 useHistory 并将我的经典组件转换为带有钩子的函数组件,现在一切都很好

丹尼尔

【讨论】:

    猜你喜欢
    • 2020-02-20
    • 2020-09-08
    • 1970-01-01
    • 1970-01-01
    • 2020-05-17
    • 2020-05-02
    • 1970-01-01
    • 2019-11-25
    • 2020-04-29
    相关资源
    最近更新 更多