Next.js: withRouter passes undefined router prop in SSR with decorated pages

Created on 5 Sep 2017  路  7Comments  路  Source: vercel/next.js

The withRouter HOC passes undefined for the router prop in SSR with custom routes. The client render works as expected, causing a React server/client checksum error.

I use next-routes, but others on Slack are having the same issue when using app.render in server.js.

  • [x] I have searched the issues of this repository and believe that this is not a duplicate.

Context

I am attempting to style active links.

Your Environment


| Tech | Version |
|---------|---------|
| next | v3.2.1 |
| node | v8.4.0 |

Most helpful comment

A simpler way to work around this is to pass the router context as the second argument to getDataFromTree:

          await getDataFromTree(
            <ApolloProvider client={apollo} store={store}>
              <ComposedComponent {...composedInitialProps} />
            </ApolloProvider>,
            {
              router: { query: ctx.query, pathname: ctx.pathname, asPath: ctx.asPath },
            },
          )

This might be related to: https://github.com/apollographql/react-apollo/issues/425

All 7 comments

I don't have experience with next-routes but I had a similar issue that might point some people who land on this issue through Google (like I did) in the right direction.

My problem manifested when trying to integrate Apollo on the server side. I had a code in a higher order component that wrapped the page component that looked like this:

const router = {query: context.query, pathname: context.pathname};
const app = (
  <ApolloProvider client={apollo}>
    <ComposedComponent url={router {...composedInitialProps} />
  </ApolloProvider>        
);
await getDataFromTree(app);

The obvious issue with this (after hitting the wall for an hour :/ ) is that it does not provide Next context to the underlying component structure. So even though I'm using this only to get GraphQL queries and not to render it as HTML, whenever some component is using withRouter higher order component it throws an error about router prop being undefined.

The solution for me was to provide router context object on the server side like so:

const router = {query: context.query, pathname: context.pathname};

const app = (
  <RouterProvider router={router}>
    <ApolloProvider client={apollo}>
      <ComposedComponent url={router} {...composedInitialProps} />
    </ApolloProvider>
  </RouterProvider>
);
await getDataFromTree(app);

<RouterProvider> is very simple component that does the following:

import React from 'react'
import PropTypes from 'prop-types'

class RouterProvider extends React.Component {
  getChildContext () {
    return {
      router: this.props.router
    }
  }
  render () {
    return this.props.children
  }
}

RouterProvider.childContextTypes = {
  router: PropTypes.object.isRequired
}

RouterProvider.propTypes = {
  router: PropTypes.object.isRequired,
}

export default RouterProvider

Hope this helps someone figure out issues with their code.

I was trying to do same thing like @jaydenseric style active links :)

@kyrisu thanks, that was really helpfull for a workaround.

Additionally I added asPath:

const url = {
  query: context.query,
  pathname: context.pathname,
  asPath: context.asPath
}

And I had to wrap <ApolloProvider> with <RouterProvider> in both getDataFromTree() and render(), to prevent React SSR warnings.

Surely this can be fixed in Next.js. At the very least, many of the examples using page decorators need to be updated to account for this issue.

router.asPath is always undefined for custom routes. I can get router.route and router.query, but never full asPath.

It is little bit complicated when we use dynamic links build with Express or another backend framework.

For example

  server.get('/place/:id', (req, res) => {
    req.query.id = req.params.id;
    return app.render(req, res, '/place', req.query);
  });

I could get full path in page component, but sending it through all components is not what i really want.

Does exist any way how to push full path from page component to withRoute HOC?

A simpler way to work around this is to pass the router context as the second argument to getDataFromTree:

          await getDataFromTree(
            <ApolloProvider client={apollo} store={store}>
              <ComposedComponent {...composedInitialProps} />
            </ApolloProvider>,
            {
              router: { query: ctx.query, pathname: ctx.pathname, asPath: ctx.asPath },
            },
          )

This might be related to: https://github.com/apollographql/react-apollo/issues/425

@radeno Just fixed the asPath server side 馃憤

This has been fixed a while ago.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jesselee34 picture jesselee34  路  3Comments

sospedra picture sospedra  路  3Comments

lixiaoyan picture lixiaoyan  路  3Comments

formula349 picture formula349  路  3Comments

rauchg picture rauchg  路  3Comments