どうもNext.jsはURLの#
以降の部分、Fragmentを使うことは推奨していないように思える。
前々からそうだったのだけれど、13で準備されているApp Routerではよりその方向性が強くなったのではないか。
Upgrading: From Pages to App | Next.js 辺りが該当する変更で、元々router.asPathで取れていたフラグメントが、usePathnameでは取れなくなっている。
とは言え、Next.js最適化ではないURLの設計をしてしまうとフラグメントを取得したい場面はある。
Next.js+App Router下ではこの位で期待の動作を得られた。
参考: How do I get the pathname with hash. · vercel/next.js · Discussion #49465 · GitHub
import { useParams, useRouter } from "next/navigation"; import { useEffect, useState } from "react"; export const useFragment = function (): [ string, (newFragment: string) => void ] { const router = useRouter(); const params = useParams(); const [fragment, setFragment] = useState(""); useEffect(() => { setFragment(decodeURIComponent(window?.location.hash ?? "").slice(1)); }, [params]); const pushFragment = (newFragment: string, scroll: boolean = false) => { router.push(`#${newFragment}`, { scroll: scroll }); }; return [fragment, pushFragment]; };
export default function HogePage() { const [fragment, pushFragment] = useFragment(); return (<>{fragment}</>); }
とは言え以下の点に気を付ける必要はある。
- サーバサイド処理(SSR/SSG)時は常に
fragment
は空 - クライアントサイド処理が行われるタイミングで改めて
fragment
が返る
という感じになるから「Next.jsはURLの#
以降の部分、Fragmentを使うことは推奨していない」のだろうか。
(どこかに書いてあるのかもしれないが、そこまで追いかけていない)
またuseParams
の振る舞いが変わったら機能しなくなるワークアラウンドである点にも厳しい。