Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

withRouter passes undefined router prop in SSR with decorated pages #2908

Closed
1 task done
jaydenseric opened this issue Sep 5, 2017 · 7 comments
Closed
1 task done

Comments

@jaydenseric
Copy link
Contributor

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.

  • 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
@kyrisu
Copy link

kyrisu commented Sep 29, 2017

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.

@huv1k
Copy link
Contributor

huv1k commented Oct 3, 2017

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

@jaydenseric
Copy link
Contributor Author

@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.

@jaydenseric jaydenseric changed the title withRouter passes undefined router prop in SSR with custom routes withRouter passes undefined router prop in SSR with decorated pages Oct 3, 2017
@radeno
Copy link
Contributor

radeno commented Oct 11, 2017

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?

@emattias
Copy link
Contributor

emattias commented Oct 20, 2017

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: apollographql/react-apollo#425

@timneutkens
Copy link
Member

@radeno Just fixed the asPath server side 👍

timneutkens pushed a commit that referenced this issue Nov 15, 2017
Add support for using withRouter as HOC with this example.

Passing router context manually fixes this, based on, #2908 (comment)
@timneutkens
Copy link
Member

This has been fixed a while ago.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants