🍇

Next.jsでページリロード時にクエリパラメータを自動削除するカスタムフック

に公開

Next.js v13以降でページリロード時に特定のクエリパラメータを自動的に削除するカスタムフックです。

以下のような場合に使用できると思います。

  1. 検索結果ページで ?q=検索ワード というクエリパラメータを使用
  2. ユーザーがページをリロード
  3. リロード後も同じ検索結果が表示され続ける

useReloadQueryCleaner

こちらのカスタムフックを使用することで実現可能です。

import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useEffect } from "react";

type PropsType = {
  /** 削除したいクエリパラメータの配列 */
  paramsToRemove?: string[];
  /** すべてのクエリパラメータを削除するかどうか */
  removeAll?: boolean;
  /** クリーニング後に実行するコールバック */
  onClean?: (() => void) | null;
};

/**
 * ページリロード時に指定したクエリパラメータを削除する高度なカスタムフック
 * - パフォーマンス最適化
 * - SSR対応
 * @param options - 設定オプション
 */
export const useReloadQueryCleaner = ({
  paramsToRemove = [],
  removeAll = false,
  onClean = null,
}: PropsType = {}): void => {
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    // SSR時は実行しない
    if (typeof window === "undefined") return;

    // リロードフラグの確認
    const reloadFlag = sessionStorage.getItem("pageReloadFlag");

    if (reloadFlag === "true") {
      // リロードフラグをリセット
      sessionStorage.removeItem("pageReloadFlag");

      let shouldClean = false;
      const newSearchParams = new URLSearchParams(searchParams.toString());

      if (removeAll) {
        // すべてのクエリパラメータを削除
        shouldClean = newSearchParams.toString().length > 0;
        newSearchParams.forEach((_, key) => {
          newSearchParams.delete(key);
        });
      } else {
        // 指定したパラメータのみ削除
        for (const param of paramsToRemove) {
          if (newSearchParams.has(param)) {
            newSearchParams.delete(param);
            shouldClean = true;
          }
        }
      }

      if (shouldClean) {
        // URLを更新
        const newUrl = newSearchParams.toString()
          ? `${pathname}?${newSearchParams.toString()}`
          : pathname;

        router.replace(newUrl);

        if (typeof onClean === "function") {
          onClean();
        }
      }
    }

    // リロード検出用のイベントリスナー
    const handleBeforeUnload = (): void => {
      sessionStorage.setItem("pageReloadFlag", "true");
    };

    window.addEventListener("beforeunload", handleBeforeUnload);

    return () => {
      window.removeEventListener("beforeunload", handleBeforeUnload);
    };
  }, [pathname, searchParams, paramsToRemove, removeAll, onClean, router]);
};

基本的な使い方

import { useReloadQueryCleaner } from '@/hooks/useReloadQueryCleaner'

const SearchPage = () => {
  // ページリロード時に 'q' パラメータを削除
  useReloadQueryCleaner({
    paramsToRemove: ['q']
  })

  return (
    <div>
      {/* ページのコンテンツ */}
    </div>
  )
}

これだけで、ページがリロードされたときに q パラメータが自動的に削除されます。

より実践的な使用例

1. 検索フォームとの組み合わせ

const SearchPage = () => {
  const [searchQuery, setSearchQuery] = useState('')
  
  // 検索クエリとページネーションのパラメータを削除
  useReloadQueryCleaner({
    paramsToRemove: ['q', 'page']
  })

  const handleSearch = (query: string) => {
    setSearchQuery(query)
    router.push({
      pathname: '/search',
      query: { q: query }
    })
  }

  return (
    <div>
      <input
        value={searchQuery}
        onChange={(e) => handleSearch(e.target.value)}
        placeholder="検索..."
      />
    </div>
  )
}

高度な使用方法

すべてのクエリパラメータを削除する

const CleanPage = () => {
  // すべてのクエリパラメータを削除
  useReloadQueryCleaner({
    removeAll: true
  })

  return (
    <div>
      {/* ページのコンテンツ */}
    </div>
  )
}

クリーンアップ後の処理を追加する

const AnalyticsPage = () => {
  useReloadQueryCleaner({
    paramsToRemove: ['utm_source'],
    onClean: () => {
      // クエリパラメータ削除後に実行したい処理
      console.log('クエリパラメータが削除されました')
    }
  })

  return (
    <div>
      {/* ページのコンテンツ */}
    </div>
  )
}

ぜひ、活用してみてください!

Discussion