16
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Next.jsのrouter.queryでURLの動的パラメータを取得するときの注意

Posted at

業務で、useEffect内でNext.jsのuseRouterを使ってクエリパラメータを取得しようとした際、意図する通りの挙動をしておらず戸惑ったので備忘録としてまとめておきます。

通常**pages/blogs/[id].tsx** のようなファイルにおいてAPIからデータを取得したい場合、getStaticPaths・getStaticPropsを使うかと思いますが、今回は画面描画してからバックグラウンドでAPIリクエストを行ってデータ取得する必要があり、そこで見つけたのがuseRouterのrouter.queryでした。

useRouterとは

そもそもuseRouterとは、Next.jsが用意しているhooksの一つで、routeに関する色んな情報を持つrouterオブジェクトへのアクセスを提供するものです。

アプリの関数コンポーネント内のrouterオブジェクトにアクセスする場合は、useRouterフックを使用できます。
useRouterはReactフックです。

https://nextjs.org/docs/api-reference/next/router#userouter

routerオブジェクトはいくつもの便利なメソッドやプロパティを持ちますが、その中のひとつにqueryがあります。

router.queryの使い方

router.query とするだけで、オブジェクト形式で動的ルーティングの中身を取得できます。

ちなみに以下の例のようにクエリパラメータが数字の場合も、string型で取得されます。

import { useRouter } from 'next/router'

// 関数コンポーネント内にて
  const router = useRouter()
  console.log(router.query)
  console.log(router.query.id)

// 出力結果(localhost:3000/blogs/1 の場合)
// { id: '1' }
// 1

useEffect内でrouter.queryを使ってみる

pages/blogs/[id].tsxのuseEffect内でrouter.queryを使ってみます。

const router = useRouter()

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

// undefined

出力されたのは undefinde でした。

どうやら、router.queryの取得はuseEffectの発火タイミングより後のようです。

【注】ただし、チームの中には上記のコードでもundefindeにならずqueryが取れている方もいましたし、私の環境でも時によってはqueryが取れることもありました。いずれにせよ不安定なものと思われるので、以下の対処をしておくのが無難なのかなと思います。
(何かご存じの方はぜひご指導・ご指摘ください。)

useEffectの依存配列にrouterを指定する

依存配列にrouterを入れてみました。

const router = useRouter() 

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

// 出力結果(localhost:3000/blogs/1 の場合)
// undefined
// 1

すると、一度undefindeと出た後にqueryが取れてコンソールに出力されました。

これで目的を果たせる場合もあるかもしれません。

しかし今回私が実装したかった要件は、取得したルートパラメーターを使ったパスでAPIへリクエストを送るというものです。
undefinedを取得してしまうと '/api/blogs/undefined'のパスにリクエストを送ってしまい、これは不都合でした。

isReadyを使ってrouter情報が準備されているか判別する

辿り着いたコードは以下の通りです。

const router = useRouter()

useEffect(() => {
  if (router.isReady) {
    const routeId = router.query.id
    console.log(routeId)
  }
}, [router])

// 出力結果(localhost:3000/blogs/1 の場合)
// 1

routerオブジェクトの持つ isReady というboolean値が最適でした。

isReadyboolean ルーターフィールドがクライアント側で更新され、使用できる状態になっているかどうか。useEffectメソッド内でのみ使用する必要があり、サーバーで条件付きでレンダリングするためには使用しないでください。

https://nextjs.org/docs/api-reference/next/router#router-object

なぜundefindeが取得されていたのか

queryの取得タイミングはuseEffectの発火よりも後なのだということは何となく察しましたが、どういう仕組みなのか気になります。

Next.jsのドキュメントのダイナミックルーティングについての箇所に、以下のように書かれていました。

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.

Pre-renderingの間はqueryは空のオブジェクトであり、hydrationのプロセスが終わった段階でqueryオブジェクトが提供されると書いてあります。

getStaticPropsやgetSererSidePropsの処理が終わるまでは、queryは空なのですね。

hydrationという単語は初めて知りましたが、Next.jsの公式の図を見るとイメージは湧きました。

pre-rendering.png
引用:https://nextjs.org/learn/basics/data-fetching/pre-rendering

参考記事

16
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?