search-params について
React開発者向けのURL検索パラメーター状態管理ガイドです。ページ内状態と移動可能なステップに応じた履歴動作の選び方を、検証付きの単一パラメーターや複数パラメーター、ハッシュナビゲーションの例で説明します。
主な機能
- 01ページ内状態と移動ステップの区別
- 02Zodによる単一パラメーターの検証
- 03replaceによる複数パラメーターの更新
- 04ハッシュ移動でのpushまたはreplaceの選択
- 05保存後のURL正規化
活用シーン
- 01フィルター、並べ替え、ページネーションの実装
- 02タブ選択状態のURL保存
- 03戻る操作に対応する複数ステップ画面の構築
- 04保存後のリソースID更新
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ promptfoo/ promptfoo --skill search-paramsインストール後、Claude Code で/search-paramsを入力して読み込まれたか確認します。
詳細
URL 検索パラメーターの状態管理
判断の指針
URL(検索パラメーターまたはハッシュ)を更新するときは、変更がページ内の状態を表すのか、ユーザーが移動できるステップを表すのかに応じて、replace と push を選択してください。
| 変更の種類 | 例 | 履歴の動作 |
|---|---|---|
| ページ内の状態 | フィルター、並べ替え、ページネーション、タブの切り替え、検索クエリ | replace - 履歴を無駄に増やさない |
| 移動可能なステップ | ウィザードの進行、複数ステップのフォーム | push - 戻るボタンで前のステップに戻れるようにする |
| 判断に迷う場合 | 選択する前に開発者に確認してください |
重要な理由: ページ内の状態変更を push すると、ブラウザーの履歴が煩雑になります。「戻る」をクリックするユーザーは、フィルターの切り替えを元に戻すのではなく、ページから移動することを期待しています。これは「戻るボタンが壊れている」バグの最大の原因です。
正しいパターン
検索パラメーターが1つの場合 - useSearchParamState を使用(推奨)
このフックは Zod で検証し、内部で常に replace: true を使用するため、履歴の動作が正しくなります。
import { useSearchParamState } from '@app/hooks/useSearchParamState';
import { z } from 'zod';
const TabSchema = z.enum(['overview', 'details', 'settings']);
const [activeTab, setActiveTab] = useSearchParamState('tab', TabSchema, 'overview');
主要ファイル: src/app/src/hooks/useSearchParamState.ts
検索パラメーターが複数の場合 - replace: true を指定して setSearchParams を使用
複数のパラメーターを一度に更新するときは、setSearchParams を直接使用します。ただし、ページ内の状態には必ず { replace: true } を渡してください。
const [searchParams, setSearchParams] = useSearchParams();
// Updating filters (in-page state -> replace)
setSearchParams(
(params) => {
params.set('status', 'active');
params.set('sort', 'name');
return params;
},
{ replace: true },
);
ハッシュベースのナビゲーション - replace または push を指定して navigate() を使用
戻るボタンでステップ間を移動できるようにするウィザードや複数ステップのフローでは、push を使用してください(デフォルトです)。
// Wizard step navigation - push so back button works between steps
// See: src/app/src/pages/redteam/setup/page.tsx
const updateHash = (newStep: string) => {
navigate(`#${newStep}`); // push (default) - intentional
};
ページ内の状態を表すハッシュ変更には replace を使用してください。
// Tab switch on a detail page - replace to avoid history clutter
navigate(`#${section}`, { replace: true });
保存後の URL 正規化
作成または保存後(ユーザー操作ではありません)に新しい ID を含めるため URL を更新する必要がある場合は、replace を使用してください。
// After first save, update URL to include new ID without adding history entry
navigate(`/evals/${newConfigId}`, { replace: true });
アンチパターン
ページ内の状態変更に push を使用する(戻るボタンが正しく動作しなくなります)
// WRONG - every filter change adds a history entry
setSearchParams((params) => {
params.set('filter', value);
return params;
});
// WRONG - navigate without replace for state change
navigate(`?tab=${newTab}`);
検証せず、1つのパラメーターに生の useSearchParams を使用する
// WRONG - no validation, easy to forget { replace: true }
const [searchParams, setSearchParams] = useSearchParams();
const tab = searchParams.get('tab');
const setTab = (v: string) => {
setSearchParams((p) => {
p.set('tab', v);
return p;
});
};
// RIGHT - use the hook instead
const [tab, setTab] = useSearchParamState('tab', TabSchema, 'overview');
null の代わりに空文字列を使用する
// WRONG - useSearchParamState will throw an invariant error
setTab('');
// RIGHT - use null to clear a param
setTab(null);
主要ファイル
src/app/src/hooks/useSearchParamState.ts- 主要なフック(内部で replace を使用)src/app/src/pages/eval/components/ResultsView.tsx-{ replace: true }の正しい使用例src/app/src/pages/redteam/setup/page.tsx- ウィザードのステップで意図的に push を使用する例