Using Next 9's routing solution for a Universal App, "router.query" for post-SSR client-side navigation is never populated even if asPath
contains a query string for any of the following
router access methods:
import { SingletonRouter, useRouter, withRouter } from 'next/router';
Perform an initial navigation to /search?term=hello
, the server rendered page's router information contains a populated "query":
const router = useRouter();
console.log({ router }); // asPath: "/search?term=hello", pathname: "/search", query: { term: "hello" }
Then navigate to the same pathname, with a different query string: /search?term=bye
. At this point Router empties "query", but has the correct new asPath
:
const router = useRouter();
console.log({ router }); // asPath: "/search?term=hello", pathname: "/search", query: {}
For the client side navigation to a route with specified query parameters such as /search?term=bye
, router.query
contains the parsed query string.
If this is not expected, it would be helpful to better document this behavior and the correct approach on the client. I would prefer to always access routing information off of the available hook / HOC rather than off window on the client to avoid a split-brain solution between the client and the server.
Hi, for the query
to be populated you must provide it to the href
value not just the asPath
. It does look like this needs to be updated in our docs to better explain this so thanks for bringing that to our attention!
Oh, that would explain the behavior we are seeing 馃憤 We were effectively doing:
<Link href={pathname} as={pathname + '?' + query} />
Hi, for the
query
to be populated you must provide it to thehref
value not just theasPath
. It does look like this needs to be updated in our docs to better explain this so thanks for bringing that to our attention!
Hi, I used your solution and it works when i'm listening to the query change in my hook but it doesn't seem to be parsed within router events (routeChangeComplete)
https://github.com/zeit/next.js/issues/9574#issuecomment-575327744
@ijjk thank you, it helped me.
Is there an example of how this would work with router.push
instead of a Link
?
Been trying to work this one out but so far no luck. For now, just doing the parsing of asPath
parameter but that does not feel optimal
Router.push(href, as)
is the signature, it works in the same way as <Link>
So:
Router.push('/abc?hello=123', '/abc?hello=123')
@timneutkens If you path first parameter as a full path it will make a hard refresh instead of client-side update, which goes around the issue but not necessarily fixing it.
Example I have is in line with docs, with dynamic paths:
Router.push('/post/[pid]', '/post/abc')}
Works in the same way as the earlier reply:
Router.push('/post/[pid]?hello=123', '/post/abc?hello=123')}
Aaaah, href
is the first parameter so needs to have query parameres - it finally clicked and works as expected now :) Thanks @timneutkens 鉂わ笍
This will work for single or multiple query params as well dynamic routing:
const query = {
param1: 'foo', param2: 'bar'
}
const url = { pathname: '/search/[id]', query };
const urlAs = { pathname: '/search/1234', query }
router.push(url, urlAs);
Output:
/search/1234?param1=foo¶m2=bar
I believe this is a relevant issue #16019
I don't think this is an issue anymore, our docs have been updated and we added more clear examples for the usage of next/link.
For client side navigations you have to provide the query
parameters required by the page, and if a page is statically generated you should only try to read the query parameters after hydration (i.e inside useEffect
) because when we generate the page we don't know anything about the query parameters or hash, or anything else more than the path of the page, with the exceptions of:
getStaticPaths
will get the query parameter that is used for that page populated. E.g /blog/[post].js
might have { query: { post: 'my-post' } }
populated if getStaticPaths
defines my-post
in the returned list of paths.getServerSideProps
have access to the query before hydration because the page is generated per-request in a serverless function, instead of at build time.If any of the above behaviors doesn't match what you currently have on your application (or our docs do a bad job) feel free to comment it out again with a reproduction and I'll re open the issue. Thank you.
i think the docs could maybe add some clarification to here. i've written down my understanding of this in this discussion
Most helpful comment
Hi, for the
query
to be populated you must provide it to thehref
value not just theasPath
. It does look like this needs to be updated in our docs to better explain this so thanks for bringing that to our attention!