Skip to content Skip to sidebar Skip to footer

Server Rendering With React-router V4 And Express.js

I'm trying to set up server-side rendering with the newest version of react-router v.4. I followed this tutorial https://react-router.now.sh/ServerRouter. I get following error

Solution 1:

Solved !

First problem was that I had spaces around <Routes /> tag.

Correct solution:

<ServerRouterlocation={req.url}context={context}><Routes /></ServerRouter>);

Second problem was in included <Header /> tag in routes.jsx file.

I had the following error (Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of StatelessComponent)

File Header.jsx contained the following line of code:

importLinkfrom'react-router';

Correct solution: (I forgot to put curly brackets ):

import { Link } from'react-router';

Solution 2:

The big issue is that the<BrowserRouter> is only expected to have one child, so you should wrap it's children in a div. This is done so that React Router is environment agnostic (you can't render a div in React Native, so RR expects you to include the appropriate wrapper).

exportdefault () =>
  <BrowserRouter><div><Header /><Matchpattern="/"component={Home} /><Matchpattern="/about"component={About} /><Misscomponent={NotFound} /></div></BrowserRouter>;

As a secondary issue, you are including the <BrowserRouter> in your <App> component, so it will be rendered on the server. You do not want this. Only the <ServerRouter> should be rendered on the server. You should move the <BrowserRouter> further up your client side component hierarchy to avoid this.

// Appexportdefault () =>
  <div><Header /><Matchpattern="/"component={Home} /><Matchpattern="/about"component={About} /><Misscomponent={NotFound} /></div>;

// index.jsrender((
  <BrowserRouter><App /></BrowserRouter>
), document.getElementById('app'))

Solution 3:

because BrowserRouter doesn't exist on react-router, try install and import it from react-router-dom

Solution 4:

I believe the answers listed above is outdated. As of today, the official react-router docs suggest using StaticRouter instead of ServerRouter for server side rendered apps.

A fantastic documentation can be found here.

Solution 5:

For anybody coming later, Ryan Florence has added a snippet of how to accomplish this.

SSR in React Router v4

// routes.jsconst routes = [
  {
    path: '/',
    component: Home,
    exact: true
  },
  {
    path: '/gists',
    component: Gists
  },
  {
    path: '/settings',
    component: Settings
  }
]

// componentsclassHomeextendsReact.Component {

  // called in the server render, or in cDMstaticfetchData(match) {
    // going to want `match` in here for params, etc.returnfetch(/*...*/)
  }

  state = {
    // if this is rendered initially we get data from the server renderdata: this.props.initialData || null
  }

  componentDidMount() {
    // if rendered initially, we already have data from the server// but when navigated to in the client, we need to fetchif (!this.state.data) {
      this.constructor.fetchData(this.props.match).then(data => {
        this.setState({ data })
      })
    }
  }

  // ...
}

// App.jsconstApp = ({ routes, initialData = [] }) => (
  <div>
    {routes.map((route, index) => (
      // pass in the initialData from the server for this specific route
      <Route {...route} initialData={initialData[index]} />
    ))}
  </div>
)

// server.jsimport { matchPath } from'react-router'handleRequest((req, res) => {
  // we'd probably want some recursion here so our routes could have// child routes like `{ path, component, routes: [ { route, route } ] }`// and then reduce to the entire branch of matched routes, but for// illustrative purposes, sticking to a flat route configconst matches = routes.reduce((matches, route) => {
    const match = matchPath(req.url, route.path, route)
    if (match) {
      matches.push({
        route,
        match,
        promise: route.component.fetchData ?
          route.component.fetchData(match) : Promise.resolve(null)
      })
    }
    return matches
  }, [])

  if (matches.length === 0) {
    res.status(404)
  }

  const promises = matches.map((match) => match.promise)

  Promise.all(promises).then((...data) => {
    const context = {}
    const markup = renderToString(
      <StaticRoutercontext={context}location={req.url}><Approutes={routes}initialData={data}/></StaticRouter>
    )

    if (context.url) {
      res.redirect(context.url)
    } else {
      res.send(`
        <!doctype html>
        <html>
          <div id="root">${markup}</div>
          <script>DATA = ${escapeBadStuff(JSON.stringify(data))}</script>
        </html>
      `)
    }
  }, (error) => {
    handleError(res, error)
  })
})

// client.jsrender(
  <Approutes={routes}initialData={window.DATA} />,
  document.getElementById('root')
)

Post a Comment for "Server Rendering With React-router V4 And Express.js"