Next.js: get query object by Router.query empty in 9.2.1

Created on 13 Feb 2020  路  21Comments  路  Source: vercel/next.js

Bug report

get query object by Router.query empty in 9.2.1

Describe the bug

when i use dynamic-routing, i come across that bug,
I would get query object by Router.query in componentDidMount func;
like this
public componentDidMount() {
console.log('Router.query', Router.query);
}
the query is empty

To Reproduce

Go to example/dynamic-routing
modify the comment.js like as:

import Router from 'next/router';
class Comment extends React.Component<{}, {}> {
    public componentDidMount() {
       console.log('Router.query', Router.query);
    }
    public render() {
      return (
        <>
          <h1>Post</h1>
        </>
      );
    }
  }

Expected behavior

get query object by Router.query

System information

  • OS: macOS
  • Browser : chrome
  • Version of Next.js: 9.2.1

Most helpful comment

If like me, you are using getStaticProps, the docs state:

Pages that are statically optimized by Automatic Static Optimization will be hydrated without their route parameters provided, i.e query will be an empty object ({}).

After hydration, Next.js will trigger an update to your application to provide the route parameters in the query object.

This means you can watch router.query in useEffect to check the params like so:

const router = useRouter();
useEffect(() => {
  console.log(router.query);
}, [router.query]);

Or watch for a specific param:

const router = useRouter();
useEffect(() => {
  console.log(router.query.myParam);
}, [router.query.myParam]);

All 21 comments

I 'm also having this issue with 9.2.1
When I redirect from page A to page B by next/link, the router.query (from next/router) contains the data I want. However, when I refresh page B on browser, the router.query becomes empty.

@jamesmosier answer is only for logging the router.query, but the point is router.query is empty, so logging it doesn't fix the issue. I think it may be a bug.

Let me provide my code for reproduce:

import React, { Component } from 'react';
import { withRouter } from 'next/router';
import Link from 'next/link';
import { getBlogs } from '../../utils/blogs';
import './style.scss';

class Blogs extends Component {
    render() {
        const blogs = getBlogs();

        return (
            <main className="blogs-container">
                {blogs.map(blog => (
                    <Link key={blog.id} href="/blog/[url]" as={`/blog/${blog.url}`}>
                        <a>{blog.title}</a>
                    </Link>
                ))}
            </main>
        )
    }
}
export default withRouter(Blogs);
import React, { Component } from 'react';
import { withRouter } from 'next/router';
import './style.scss';

const countdownInterval = 1000;

class Game extends Component {
    constructor(props) {
        const { router } = props;
        super(props);
        console.log(router.query)
    }

    render() {
        return (
            <div>
                some content here...
            </div>
        )
    }
}

export default withRouter(Game);

Flow:

  1. Click one of the blog link on the blogs page
  2. Redirected to blog page
  3. The console log router.query correctly
  4. Refresh the page (blog page)
  5. The console shows router.query as empty

This is correct behavior and documented here: https://nextjs.org/docs/routing/dynamic-routes#caveats

Pages that are statically optimized by Automatic Static Optimization will be hydrated without their route parameters provided, i.e query will be an empty object ({}).

After hydration, Next.js will trigger an update to your application to provide the route parameters in the query object.

This is correct behavior and documented here: https://nextjs.org/docs/routing/dynamic-routes#caveats

Pages that are statically optimized by Automatic Static Optimization will be hydrated without their route parameters provided, i.e query will be an empty object ({}).

After hydration, Next.js will trigger an update to your application to provide the route parameters in the query object.

thanks for helping. I just read the documentation again. It states that add getInitialProps could disable Automatic Static Optimization and fix router.query empty issue. I just tried to add it but still not working.

@ivanhoe-dev Please provide a reproduction so we can take a look and see what's wrong 馃檹

Closing as the OP issue was identified to be due to Automatic Static Optimization.

@ivanhoe-dev, per above by @lfades, provide a demo and we can take a look for you.

I just fixed the issue by changing my own code. I move the router.query related code from constructor to render function. It seems because when refreshing page, the router.query is empty initially. After I put it in the render function, it will re-render the page after router.query is updated.

I still have this problem after running build (export) even though in console i see:

Warning: You have opted-out of Automatic Static Optimization due to `getInitialProps` in `pages/_app`.

go to: https://codesandbox.io/s/with-static-export-39rhb?fontsize=14&hidenavigation=1&theme=dark
, open console and add some query param to url

@Timer @lfades

If you believe you've found a real bug with a full reproduction please open a new issue.

I used this method for class component:

import React, {Component} from 'react'
import {withRouter} from 'next/router';

export async function getServerSideProps(content) {
const {urlParam} = content.query;
return {props: {urlParam}}
}

...
render(){
return <h2>{this.props.urlParam}</h2>
}
...

export default withRouter(Comp);

Works after reload, hard reload too

This is an issue. In class components when we use withrouter along side static props, the query object is empty

I got the same problem..
I am sure this is the bugs

But for now I use something like this as workaround:

setTimeout(() => {
    const { token, email } = this.props.router.query;

    ....

}, 0);

Same bug here. When console-logging router object, I can see the router.query exists, but when I try to grab the parameters from the router.query object, I get undefined. For now, I'm using window object...

Same issue - router.query is empty on mount and an undetermined amount of time after. Option is to place it within a useEffect in which case it causes multiple re renders, or simply not use it.

Hi @joelwass
You can try my workaround above.. it works for me until this problem is solved

I got the same problem..
I am sure this is the bugs

But for now I use something like this as workaround:

setTimeout(() => {
    const { token, email } = this.props.router.query;

    ....

}, 0);

This trick saddly doesn't work in my functional component ...
i used it in useEffect() but when i refresh the page, my query still turn to null

@concat1911 the workaround above is for class component..

I think this issue (null routing query) does not occur in functional component, as long as you use hooks.
Example:

const router = useRouter();
const { myRoutingParam, anotherParams } = router.query;

I believe you can always use window object and parse query params by yourself, e.g.

// URL: http://localhost:3000/sth?page=2
const SomeComponent = () => {
  const router = useRouter();
  useEffect(() => {
    console.log(new URLSearchParams(window.location.search).get("page"));
  }, []);
}

That seems to be a better solution than hacking with the event loop

For anyone still running into this issue, a full production build works for us meanwhile local dev gives a query of {}.

The following code cannot find input when running next, but works perfectly with next build && next start

    componentDidMount() {
        // does not work on dev, only when deployed
        const input = this.props.router.query.input;
        input && this.searchOnLoad(input);
    }

We're on [email protected]

If like me, you are using getStaticProps, the docs state:

Pages that are statically optimized by Automatic Static Optimization will be hydrated without their route parameters provided, i.e query will be an empty object ({}).

After hydration, Next.js will trigger an update to your application to provide the route parameters in the query object.

This means you can watch router.query in useEffect to check the params like so:

const router = useRouter();
useEffect(() => {
  console.log(router.query);
}, [router.query]);

Or watch for a specific param:

const router = useRouter();
useEffect(() => {
  console.log(router.query.myParam);
}, [router.query.myParam]);

Then How to render component depends on query params?

function SomeActiveLink() {
  const router = useRouter()
  const { view } = router.query
  return (
        <Link href="/some-href"}>
          <a className={view==='active' ? 'active' : ''}>Active</a>
        </Link>
  )
}

I'm getting this warning because query is empty object on the server, any help is appreciated.

react_devtools_backend.js:2450 Warning: Prop className did not match. Server: "active" Client: "";

Running into the same issue in development. I was trying to load a query parameter from the url when the component init, and use it as initial value for a hook:

const [searchFilter, setSearchFilter] = useState<string>(setSearchFilter(resolveInitialSearchFilter(router))');
console.log('router', router';

But this didn't work. The router log shows 3 logs and the first one is systematically empty. (router.query is an empty object, while router.path contains the proper data).

Using @minlare trick with useEffect worked around the problem.

const router: NextRouter = useRouter();
const [searchFilter, setSearchFilter] = useState<string>('');

useEffect(() => {
  setSearchFilter(resolveInitialSearchFilter(router));
}, [router?.query?.[QUERY_SEARCH_FILTER_KEY]]);
Was this page helpful?
0 / 5 - 0 ratings