Next.js: Not getting `url.query` in custom parametrised server route

Created on 11 Feb 2018  路  19Comments  路  Source: vercel/next.js

Expected behavior

Got to detail/post-id and see rendered post detail. (server should wait until data is fetched and then return post detail)

Actual behavior

Go to detail/post-id and see spinner then see post detail. (server does not wait until data is fetched and renders spinner client then renders post detail)

image

Most helpful comment

Thank you @developer239 !

Your solution was ok, but the url should be placed on composedInitialProps after the

if (ComposedComponent.getInitialProps) {
  composedInitialProps = await ComposedComponent.getInitialProps(ctx)
}

otherwise it would be replaced.

All 19 comments

Hello!, some questions:

Are you sending url in props ? (I can't see it anywhere)
Does it happens always or only on client side navigation ?

Hey,
what do you mean by Are you sending url in props ? (I can't see it anywhere)?

Client-side gets url in props.

Server-side does not get url in first render. It gets url in props in 2nd+ renders.

So what basically happens is:
1) Go to post detail
2) Server fails fetching post detail because it does not have the url prop
3) Server renders loader
4) Client fetches post detail
5) When you look at source code you see loading... (loader) not post data

Oh, I'm sorry for not being clear.
The issue is that ComposedComponent is only getting the props inside { ...composedInitialProps } and unless you have url there you're actually not sending url to the component during the execution of getDataFromTree that happens before getInitialProps ends.

Oh cool, I thought about that too but I was not sure if that is the right way to go. Could you please help me to fix the problem?

I changed my detail page component like this:

class DetailPage extends React.Component {

  static getInitialProps = (ctx) => {
    console.log('firing get initial props', ctx)
    return {}
  }

  render() {
    const { data } = this.props

    return (
      <div>
        <Header />
        {JSON.stringify(data)}
      </div>
    )
  }
}

const enhance = compose(
  withApollo,
  graphql(queryPostDetail, {
    options: ownProps => ({
      // a: console.log('own props', ownProps),
      variables: {
        id: ownProps.url.query.id,
      },
    }),
  }),
  apolloSpinner(),
  apolloError(),
)

export default enhance(DetailPage)

But when I do this:

console.log('ComposedComponent.getInitialProps ', ComposedComponent.getInitialProps)

It returns undefined.

How do I make the static getInitialProps props work with other HOC? :)

It looks like withApollo HOC does nto see the getInitialProps because of graphql HOC.

try to add url inside Apollo's getInitialProps, otherwise you'll need to clone statics all the way up to withApollo
like this:

getInitialProps({ query, pathname }) {
  const url = { query, pathname }
  // ... apollo stuff
}

Like this?

    const url = { query: ctx.query, pathName: ctx.pathName }
      let serverState = {
        apollo: {
          data: {},
        },
      }

      let composedInitialProps = {
        url,
      }
      if (ComposedComponent.getInitialProps) {
        composedInitialProps = await ComposedComponent.getInitialProps(ctx)
      }

This seems to work fine now. Thanks a lot. :)

However, do I have to use route masking for client-side router?

<Link as={'detail/cjdg4ro6z2zfb0154l6jubt89'} href={`/detail?id=cjdg4ro6z2zfb0154l6jubt89`}>
  <a>post</a>
</Link>

Otherwise it looked like nextjs fetched whole document and I did not see the loader.

the thing there is that Next only knows the page /detail, /detail?id=cjdg4ro6z2zfb0154l6jubt89 is the same page but with a query param but detail/cjdg4ro6z2zfb0154l6jubt89 is a different page for Next.

Basically, you need to send more parameters to <Link> like here or use a lib like next-routes

I have the same problem as you @developer239 but putting

const url = {
  asPath: ctx.asPath,
  pathname: ctx.pathname,
  query: ctx.query,
}
let composedInitialProps = { url }

inside the getInitialProps function in the withData lib HOC doesn't solve my problem.

I have:

export default compose(
  withData,
  withRedux(createStore, mapStateToProps),
  graphql(singleProduct, {
    options: ({ url }) => ({ 
      variables: { 
        seoName: url.query.productSeoName,
        crossSellingLimit: 12
      } 
    }),
    props: ({ data: { loading, error, product } }) => ({
      loading,
      error,
      product
    })
  }),
  [...]
)(MyComponent)

but server side I have the
error: TypeError: Cannot read property 'query' of undefined at options
error, so the query is sent by the client.

Did you do anything else to solve your problem?

Thank you,
Matteo

@f2net I believe because of withRedux(createStore, mapStateToProps), you are loosing your props you have to call WrappedComponent.getInitialProps too.

Thank you for your quick answer: sorry, where do I have to make that call?
I am using the withApollo example's withData.

Thank you very much

Can you share your withRedux HOC?

@f2net That is weird. I thought you have your own redux wrapper. This should work as expected. I will look into it later this week. :)

@developer239 Thank you very much!

@developer239 I opened issue 4030, where I posted the results of logging around:
https://github.com/zeit/next.js/issues/4030

Thank you @developer239 !

Your solution was ok, but the url should be placed on composedInitialProps after the

if (ComposedComponent.getInitialProps) {
  composedInitialProps = await ComposedComponent.getInitialProps(ctx)
}

otherwise it would be replaced.

@f2net That is good to know. Thank you :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

timneutkens picture timneutkens  路  3Comments

wagerfield picture wagerfield  路  3Comments

kenji4569 picture kenji4569  路  3Comments

jesselee34 picture jesselee34  路  3Comments

pie6k picture pie6k  路  3Comments