koudenpaのブログ

趣味のブログです。株式会社はてなでWebアプリケーションエンジニアをやっています。職業柄IT関連の記事が多いと思います。

Next.jsのApp Router下でハッシュフラグメントを得る

どうも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の振る舞いが変わったら機能しなくなるワークアラウンドである点にも厳しい。

やはりフレームワークを使うならフレームワークの機能内で構成するのが良いのだろう。