0
votes

I'm doing server side rendering for my React Js App.when i run my App,I'm getting bellow error.

TypeError: _Router2.default.computeRootMatch is not a function at /home/../web/node_modules/react-router-config/matchRoutes.js:20:24 at Array.some (native) at matchRoutes (/home../web/node_modules/react-router-config/matchRoutes.js:18:10) at /home/../web/build/bundle.js:2250:53 at Layer.handle [as handle_request] (/home/../web/node_modules/express/lib/router/layer.js:95:5) at next (/home/../web/node_modules/express/lib/router/route.js:137:13) at Route.dispatch (/home../web/node_modules/express/lib/router/route.js:112:3) at Layer.handle [as handle_request] (/home../web/node_modules/express/lib/router/layer.js:95:5) at /home../web/node_modules/express/lib/router/index.js:281:22 at param (/home/../web/node_modules/express/lib/router/index.js:354:14)

server.js file

import express from 'express';
import { matchRoutes } from 'react-router-config';
import Routes from './client/Routes';
import renderer from './helpers/renderer';
import createStore from './helpers/createStore';
const app = express();

app.use(express.static('public'));
app.get('*', (req, res) => {
  const store = createStore(req);

  const promises = matchRoutes(Routes, req.path)
    .map(({ route }) => {
      return route.loadData ? route.loadData(store) : null;
    })
    .map(promise => {
      if (promise) {
        return new Promise((resolve, reject) => {
          promise.then(resolve).catch(resolve);
        });
      }
    });

  Promise.all(promises).then(() => {
    const context = {};
    const content = renderer(req, store, context);

    if (context.url) {
      return res.redirect(301, context.url);
    }
    if (context.notFound) {
      res.status(404);
    }

    res.send(content);
  });
});

app.listen(3000, () => {
  console.log('Listening on prot 3000');
});
2

2 Answers

3
votes

I know where it's failing because I just did this same Udemy course. If youre upgrading react-router-config your root node for your Routes array needs a path defined. Per:https://github.com/ReactTraining/react-router/issues/6381. So something like:

export default [
  {
    ...App,
    path: '/',
    routes: [
      {
        ...HomePage,
        path: '/',
        exact: true
      },
      {
        ...AdminsListPage,
        path: '/admins'
      },
      {
        ...UsersListPage,
        path: '/users'
      },
      {
        ...NotFoundPage
      }
    ]
  }
];

should do the trick.

1
votes

I have the same use case and can't get it work. I am using webpack. With webpack 3 this code is working fine:

import { h } from 'preact';
import render from 'preact-render-to-string';
import {routes, createHistory, makeStore, AppBar, NavBar} from '../dist/ssr-bundle'
import {renderRoutes, matchRoutes} from 'react-router-config';
import {ConnectedRouter} from 'connected-react-router';
import {Provider} from 'react-redux';
import {ServerStyleSheet} from 'styled-components';
import fs from 'fs';
import mapping from '../dist/assets-manifest.json';

const prerenderIndex = fs.readFileSync('./dist/index.html', 'utf8');
const html = prerenderIndex
  .replace(/\<style data-styled-components=".*\<\/style\>/g, 'ssrstyledcomponents')
  .replace(/\<body\>.*\<\/body\>/g, 'ssrcontent');

const loadRouteDependencies = (location, store) => {
  const dataRequirements = matchRoutes(routes, location)
      .filter(route => route.route.component && route.route.component.loadData) // filter components with loadData
      .map(route => route.route.component.loadData(store, route.match)); // dispatch data requirement
  return Promise.all(dataRequirements);
}

const loadRouteChunks = (location) => {
  const neededChunks =  matchRoutes(routes, location).map(route => route.route.name); // route.route.name defined with webpack4
  return neededChunks; //returns undefined with webpack4
}

export const handleRender = (req, res) => {
  const history = createHistory({initialEntries: [req.originalUrl]});
  const store = makeStore(history, req.originalUrl);

  const chunks = loadRouteChunks(req.originalUrl)
    .map(item => mapping[item])
    .map(item => `<script defer="defer" src="/${item}"></script>`)
    .join(' ');

  loadRouteDependencies(req.originalUrl, store).then(() => {
    const App = (
      <Provider store={store}>
        <div id="app">
          <ConnectedRouter history={history}>
            <div>
              <AppBar size={'large'}/>
                {renderRoutes(routes)}
              <NavBar/>
            </div>
          </ConnectedRouter>
        </div>
      </Provider>
    )

    const sheet = new ServerStyleSheet()
    const body = render(sheet.collectStyles(App));
    const redux = `<script>window.REDUX_DATA = ${JSON.stringify(store.getState())}</script>`;
    const doc = `<body>${body}<script></script>${redux}</body>`;
    const document = html
      .replace(/ssrstyledcomponents/, sheet.getStyleTags())
      .replace(/ssrcontent/, doc)
      .replace(/\<script\>\<\/script\>/, chunks);
    res.status(200).send(document);
  });
}

Since I am using webpack 4, the code does not work anymore and throws the same Type Error. Im not sure why this occurs, because the really strange thing is that a code, where matchRoutes is called only in loadRouteDependencies is running without error and behaves correctly.

const chunks = loadRouteChunks(req.originalUrl)
    .map(item => mapping[item])
    .map(item => `<script defer="defer" src="/${item}"></script>`)
    .join(' ');