まくろぐ

Next.js でハッシュフラグメントを扱う(useHash カスタムフック)

更新:
作成:

ハッシュフラグメントとは

URL のハッシュフラグメントというのは、下記のような URL の末尾の # 以降の部分を指します。

https://examle.com/sample#AAA

似たようなものにクエリパラメーター(?key=val のみたいなの)もありますが、ハッシュフラグメントは HTTP リクエスト時に、その文字列(上記の例では AAA)がサーバーに送られないという違いがあります。 つまり、ハッシュフラグメントの値は、クライアントサイドで使用することが意図されています。

useHash フックの実装

下記の useHash 関数は、Next.js でハッシュフラグメントを簡単に扱えるようにするカスタムフックの例です。 useState フックと同じ感覚で使えるように、ハッシュフラグメントの現在値と、設定用の関数をペアで返します。

src/libs/useHash.ts
import { useCallback } from 'react'
import { useRouter } from 'next/router'

/**
 * URL のハッシュフラグメント部分を扱うためのフックです。
 *
 * 次のようにすると、`hash` 変数に URL の `#` 以降の値が格納されます。
 * URL の `#` 以降の値を変更したときは、`setHash` 関数を使用します。
 *
 * ```
 * const [hash, setHash] = useHash()
 * ```
 */
export function useHash(): [string, (newHash: string) => void] {
  const router = useRouter()
  const hash = extractHash(router.asPath)
  const setHash = useCallback((newHash: string) => {
    // ブラウザの履歴に残すなら、ここを router.push に変えれば OK
    router.replace({ hash: newHash }, undefined, { shallow: true })
  }, [])
  return [hash, setHash]
}

// URL の # 以降の文字列を取り出すユーティリティ
function extractHash(url: string): string {
  return url.split('#')[1] ?? ''
}

やっていることは簡単で、router.asPath から抽出したハッシュフラグメントの値を返しているだけです。 レンダリングのたびに値を抽出して返そうとしますが、まぁこれくらいはよいかなと(^^;

ちゃんとハッシュフラグメントの変化だけを監視するのであれば、次のような感じで router.events でイベントをハンドルすべきかもしれません。

router.events.on('hashChangeComplete', handleHashChange)

useHash フックの使用例

下記は useHash フックの使用例です。

/p/k2ahpw5/img-001.png

テキストボックスに何か入力すると、Web ブラウザの URL のハッシュフラグメント部分がそれに合わせて変化し、さらにページ内の表示(Current hash の値)も連動して変化します。

src/pages/sample.tsx
import { FC } from 'react'
import { useHash } from '../libs/useHash'

const SamplePage: FC = () => {
  const [hash, setHash] = useHash()

  return (
    <>
      <p>Current hash = {hash}</p>
      <input onChange={(e) => setHash(e.target.value)} />
    </>
  )
}

export default SamplePage

関連記事

まくろぐ
サイトマップまくへのメッセージ