業務で、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値が最適でした。
isReady
:boolean
ルーターフィールドがクライアント側で更新され、使用できる状態になっているかどうか。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の公式の図を見るとイメージは湧きました。
引用:https://nextjs.org/learn/basics/data-fetching/pre-rendering
参考記事