2

I am following this tutorial - https://www.youtube.com/watch?v=Smk2FusU_70 (Right at the 28:38 mark)

However it came out before v4 and I'm getting an error:

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

I saw in this answer it looks like he's having a similar issue - What's wrong with this ReactRouter.match() implementation?

Although I'm not quite sure make the change in the else{} statement for match({})

What should I do to make it work?

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)

My package.json file

{
  "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"
  }
}
7
  • what version of react-router are you using? Commented Nov 27, 2017 at 17:11
  • 4.2 added my package.json file Commented Nov 27, 2017 at 17:16
  • mount does not exist in 4.2. seems like your tutorial is using v3.x. You should install 3.x instead if you want the tutorial code to work. v3 -> v4 was a large breaking change to many react-router apis. Commented Nov 27, 2017 at 17:17
  • where did I use mount? What can I do to make it work for 4.2 Commented Nov 27, 2017 at 17:18
  • sorry, I meant match :P Commented Nov 27, 2017 at 17:22

1 Answer 1

2

Use matchPath for React Router 4+

As noted above, though off topic from your question... you should be using StaticRouter for 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>`);

    });
});

I use this to call the initialAction method (which is static) on any components where I might want to preload the data for the component on the server side. However you can write all of your components without this method and the code below will work just the same.

Note that the routes is an object in another file e.g.

import Home from "./components/Home";

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

export default routes;
Sign up to request clarification or add additional context in comments.

8 Comments

can you give me a code example of how that would work with matchPath?
Sure give me a few minutes to get back to my desk. I’ll amend this answer with an example and then comment again when it’s done. If it helps, please mark the answer as accepted :)
thanks, trying it now. How do you import StaticRoute? const reactRouter = require('react-router'), StaticRoute = reactRoute.StaticRoute?
import { StaticRouter, matchPath } from "react-router-dom";
sorry, yea, im a bit confused by this :-( not sure how to make it work with the rest of the code. github.com/RubikCubes/reactSSR
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.