【问题标题】:react + SSR: TypeError: match is not a functionreact + SSR: TypeError: match is not a function
【发布时间】:2017-11-27 17:07:39
【问题描述】:

我正在关注本教程 - https://www.youtube.com/watch?v=Smk2FusU_70(正好在 28:38 标记)

但是它是在 v4 之前发布的,我收到了一个错误:

/Users/morganallen/Desktop/react_ssr/myapp/server/index.js:55
            match({routes, location: req.url}, (error, redirect, ssrData) => {
            ^

TypeError: match is not a function
    at fs.readFile (/Users/morganallen/Desktop/react_ssr/myapp/server/index.js:55:13)
    at tryToString (fs.js:455:3)
    at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:442:12)

我在这个答案中看到他似乎遇到了类似的问题 - What's wrong with this ReactRouter.match() implementation?

虽然我不太确定在else{} 语句中对match({}) 进行更改

我应该怎么做才能让它工作?

require('import-export')
require('babel-core/register')({presets: ['es2015', 'react']})

const http = require('http')
const path = require('path')
const fs = require('fs')
const express = require('express')
const react = require('react')
const reactRouter = require('react-router')
const reactDomServer = require('react-dom/server')


const renderToString = reactDomServer.renderToString


const match = reactRouter.match

const RouterContext = reactRouter.RouterContext

const staticFiles = [
    '/static/*',
    '/logo.svg',
    '/asset-manifest.json',
    '/favicon.ico'
]

const app = express()

app.server = http.createServer(app)

app.use(express.static('../build'))


staticFiles.forEach(file => {
    app.get(file, (req, res) => {
        const filePath = path.join(__dirname, '../build', req.url)
        res.sendFile(filePath)
    })
})

const routes = require('../src/routes').default()

app.get('*', (req, res) => {

    const error = () => res.status(404).send('404')

    const htmlFilePath = path.join(__dirname, '../build', 'index.html')

    fs.readFile(htmlFilePath, 'utf8', (err, htmlData) => {

        if(err) {
            error()
        }
        else{
            match({routes, location: req.url}, (error, redirect, ssrData) => {
                if(error){
                    error()
                }
                else if(redirect){
                    res.redirect(302, redirect.pathname + redirect.search)
                }
                else if(ssrData){
                    const ReactApp = renderToString(react.createElement(RouterContext, srrData) )
                    const RenderApp = htmlData.replace('{{SSR}}', ReactApp)
                    res.status(200).send(RenderApp)
                }
                else{
                    error()
                }
            })
        }
    })
})

app.server.listen( process.env.PORT || 8080)
console.log(app.server.address().port)

我的 package.json 文件

{
  "name": "myapp",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "babel-core": "^6.26.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "import-export": "^1.0.1",
    "react": "^16.1.1",
    "react-dom": "^16.1.1",
    "react-router": "^4.2.0",
    "react-router-dom": "^4.2.2",
    "react-scripts": "1.0.17"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

【问题讨论】:

  • 你用的是什么版本的 react-router?
  • 4.2 添加了我的 package.json 文件
  • mount 在 4.2 中不存在。好像您的教程使用的是 v3.x。如果您希望教程代码正常工作,您应该安装 3.x。 v3 -> v4 是对许多 react-router api 的重大突破。
  • 我在哪里使用了 mount?我该怎么做才能使它适用于 4.2
  • 对不起,我的意思是match:P

标签: javascript node.js reactjs


【解决方案1】:

在 React Router 4+ 中使用 matchPath

如上所述,尽管与您的问题无关……您应该将 StaticRouter 用于 React Router 4

app.get("*", ( request, response ) => {
const store = configureStore();

const promises = routes.reduce((acc, route) => {
    if (matchPath(request.url, route) && route.component && route.component.initialAction) {
        acc.push(Promise.resolve(store.dispatch(route.component.initialAction())));
    }
    return acc;
}, []);

Promise.all(promises)
    .then( () => {
        const context={};
        const markup = renderToString(
          <Provider store={store}>
              <StaticRouter location={request.url} context={context}>
                  <App />
              </StaticRouter>
          </Provider>
        );

        const initialData = {};

        response.send(`
            <!DOCTYPE html>
            <html class="no-js" lang="en">
                <head>
                </head>
                <body>  
                    <div id="root">${markup}</div>
                    <script src="/app.bundle.js" defer></script>
                    <script>window.__initialData__ = ${serialize(initialData)}</script>
                </body>
            </html>`);

    });
});

我使用它来调用任何组件上的 initialAction 方法(它是静态的),我可能希望在服务器端为组件预加载数据。但是,您可以在不使用此方法的情况下编写所有组件,并且下面的代码也可以正常工作。

请注意,路由是另一个文件中的对象,例如

import Home from "./components/Home";

const routes = [
    {
        path: "/",
        exact: true,
        component: Home
    }
];

export default routes;

【讨论】:

  • 你能给我一个代码示例,说明它如何与 matchPath 一起工作吗?
  • 请给我几分钟时间回到我的办公桌前。我会用一个例子修改这个答案,然后在完成后再次评论。如果有帮助,请将答案标记为已接受:)
  • 谢谢,现在试试。你如何导入StaticRouteconst reactRouter = require('react-router'), StaticRoute = reactRoute.StaticRoute?
  • import { StaticRouter, matchPath } from "react-router-dom";
  • 抱歉,是的,我对此有点困惑 :-( 不知道如何使它与其余代码一起工作。github.com/RubikCubes/reactSSR
猜你喜欢
  • 1970-01-01
  • 2019-04-05
  • 2021-10-26
  • 1970-01-01
  • 2021-10-02
  • 2020-03-01
  • 2018-05-02
  • 1970-01-01
  • 2021-03-06
相关资源
最近更新 更多