Next.js router.queryがundefinedになる問題

withRouterやuseRouterを使用するとファーストレンダリングでundefinedになる問題が発生する
これは公式ドキュメントにも書いてます。

自動静的最適化によって静的に最適化されたページは、ルートパラメータが提供されないままハイドレーションされます。
ハイドレーションの後、Next.jsはアプリケーションの更新をトリガーにして、クエリオブジェクトにルートパラメータを提供します。

といった事象が起きているため、ファーストレンダリング時にundefinedが発生して2回目のレンダリングからクエリが取得ができるようになります。

以下のようにすれば2回目のレンダリングで取得することが可能です。

const router = useRouter()
const userId = router.query.userId;
useEffect(() => {
  // ここでAPIリクエストなど
  userId && request()
}, [userId]);

しかしながら、これはあまりイケてる方法ではないと思います。
例えば、パラメータが存在しない場合にリダイレクト処理をかけたいケースなど、初回で弾かれてしまってリダイレクトがかかってしまいます。
レンダリング数をカウントして回避することもできますが、routerに格納されているasPathを使用することでパラメータを取得できるので、query-stringを用いてasPathからパラメータを取得してみましょう。

また使い回しができるよう独自のHOCとして扱うのがよいと思います。

import React from 'react';
import { withRouter } from 'next/router';
import queryString from 'query-string';

export interface NextPageWithRouterProps {
  router: NextRouter;
}

export type NextPageWithRouter = React.FunctionComponent<NextPageWithRouterProps>;

export const withPageRouter = (Component: NextPageWithRouter) => {
  return withRouter(({ router, ...props }) => {
    router.query = queryString.parse(router.asPath.split(/\?/)[1]) as { [key: string]: string };
    return <Component {...props} router={router} />;
  });
};

関連記事